home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-10-30 | 165.5 KB | 5,160 lines |
-
-
-
-
-
-
-
-
-
-
-
-
- ii
- iiii
- ii
- sss cccc
- ssssssss cccccccc iiiii
- sss sss ccc ccc iiiii
- sssss ccc iii
- sssss ccc iii
- sssss ccc iii
- sss sss ccc ccc iii
- ssssssss cccccccc iiiiiiiii
- ssss cccc iiiiiiiii
-
-
- Small C Interpreter
- Version 1.3 for MS-DOS
- Copyright (C) 1986 Bob Brodt
-
-
- Programmer's Manual
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Small C Interpreter Programmer's Manual
-
-
- 1. Introduction to SCI Programming
-
-
- This section of the manual is a tutorial introduction
- to the C language. If you have a casual knowledge of BASIC
- and understand some of the fundamental concepts of
- programming, you should have no difficulty in following
- along. This tutorial is designed to be used along with SCI,
- so get out your working copy of the SCI distribution
- diskette. You did make a backup copy, didn't you? If not,
- DO NOT PASS GO, DO NOT COLLECT $200 until you've read and
- followed the instructions in the Introduction section of the
- SCI User's Manual!
-
- Now go ahead and start up SCI. The interpreter should
- be loading the default "shell" file, SHELL.SCI. This file
- simply contains a C program that is run by the SCI
- interpreter. It performs several functions (most of which
- shall remain invisible to you for the moment), but the most
- important is to allow you to write and test SCI programs
- immediately. After the interpreter has started up, you
- should see SCI's program identification banner and a
- greater-than symbol (>), like this:
-
-
- A> SCI
- Small C Interpreter, V1.3 15Mar86 Copyright (C) 1986 Bob Brodt
- Small C Shell, V1.3 15Mar86 Copyright (C) 1986 Bob Brodt
- >
-
-
- The ">" tells you that SCI is now ready to accept input
- from you. One of the nicest features of SCI is its ability
- to immediately perform any C statement that you type. As
- you will learn later, every C statement produces a value as
- a side-effect. One of the functions of SHELL.SCI is to
- print this value as a decimal number after the statement has
- been executed. Thus, you could enter some arithmetic
- expression like the following:
-
-
- > 2+2;
- 4
- >
-
-
- and have the SCI shell print the result, just like
- BASIC.
-
-
-
-
-
-
- - 1 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 2. SCI Statement Structure
-
-
- In the above example, notice the semicolon at the end
- of the line. The C language allows you to write programs
- without regard to "white space" (spaces, tabs and ends of
- lines). This means that the components of program
- statements can be seperated by as many spaces or tabs as you
- like; program lines can be grouped together seperated from
- the rest of the program by blank lines, to show the reader
- that they perform a discrete function; you can indent groups
- of lines following a program looping statement to show where
- the loop starts and ends. By allowing you to "sculpture"
- your program like this, C lets you write very easy to read
- and understand programs. This is very much in contrast to
- BASIC which requires every program statement to start with a
- line number, followed by a space and then the statement all
- on a single line. Because C is such a free-form language it
- would have a difficult time recognizing the end of a
- statement without some kind of "end-of-statement" marker.
- This is the purpose of the semicolon.
-
- Now we're going to confuse you even further by telling
- you that SCI doesn't need a semicolon at the end of a
- statement! Because it's an interpreter, SCI recognizes
- either the end of a line or a semicolon as an
- end-of-statement marker. In fact, if a statement spills
- over onto another program line, SCI will complain - it
- requires that every statement be completely contained on one
- line. This restriction was imposed by the fact that SCI is
- an interpreter and not a compiler. This is an important
- difference between "SCI C" and "standard C" (which allows a
- single statement to be spread out over several lines). So
- if you are an experience "C hacker", please be aware of this
- fact.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 2 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 3. SCI Program Structure
-
-
- When learning a new programming language, it's always
- helpful to recall fundamentals and ask yourself the question
- "what is a program?". Simply stated, a program is a list of
- instructions that tell the computer exactly what to do. A
- program written in the BASIC language is an ideal example of
- this concept; a list of instructions. The instructions are
- numbered to make it easy to see the order in which they'll
- be performed. Let's examine a fragment from a BASIC program
- and identify some of its key components.
-
-
- 100 REM *** sort a list of numbers in ascending order ***
- 110 DIM NUM(100),RSP$(80)
- 120 REM get the unsorted number list
- .
- .
- .
- 220 REM got 'em, now sort 'em then print 'em
- 230 GOSUB 500
- 240 FOR I=1 TO 100
- 250 PRINT NUM(I)
- 260 NEXT I
- 270 PRINT "Got another list to sort?";
- 280 INPUT RSP$
- 290 IF RSP$="Y" THEN GOTO 120
- 300 END
- 500 REM *** bubble sort routine ***
- 510 REM sorts the numbers in the array "NUM"
- .
- .
- .
- 600 RETURN
-
-
- Even the novice BASIC programmer can glance at this
- program fragment and tell what's happening: it starts with
- line 100, which is a note to the (human) reader telling him
- what the program intends to do - sort a bunch of numbers in
- ascending order. Line 110 tells the computer to reserve
- some memory storage we'll need later. Remember that BASIC
- allows variables to be "known" to every instruction in the
- program. Thus, you (the programmer) can not effectively
- control and limit access to variables. This makes it
- difficult at times to determine where in the program a
- variable is being set when it shouldn't be. This is a very
- important difference between BASIC and C, as you will find
- out later.
-
- The word "GOSUB" at line 230 tells the computer to hold
-
-
- - 3 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- its place at the current location in the program, then jump
- to instruction number 500. The "RETURN" at line 600
- corresponds to the "GOSUB" and tells the computer to
- continue with the instruction following the "GOSUB". Notice
- that the set of instructions from line 500 to 600 are
- general-purpose in nature and could possibly be used in
- another BASIC program that required a number sorting
- function. However, to interface this sub-program to another
- program would probably require modifications to either the
- other program or the sub-program, or both. This makes the
- thought of extracting the number sorting function somewhat
- less attractive.
-
- Now look at the instructions from line 270 to 290.
- Essentially, these ask the program user if there are any
- more numbers to sort, and jump back to the beginning of the
- program to start the process all over again. But, what if
- the programmer decides at some later time to modify the
- program and accidentally deletes line 120 - the target of
- the "GOTO" instruction at 290. BASIC would be totally
- confused, since it wouldn't be able to find line 120
- anymore. Although numbering program instructions, like
- BASIC does, is very nice and neat and makes a program easy
- for the human reader to follow, it can become unmanageable
- as the program grows in complexity.
-
- Now let's take a look at the comparable program
- fragment written in C. Please don't be concerned with the
- details of this program at the moment, but rather focus on
- the overall structure:
-
-
- # *** sort a list of numbers in ascending order ***
- main()
- {
- char num[100], rsp[80];
-
- while ( 1 )
- {
- # get the unsorted number list
- .
- .
- .
- # got 'em, now sort 'em then print 'em
- sort( num );
- i=0;
- while ( i<100 )
- {
- printf( "%d\n", num[i] );
- ++i;
- }
- puts( "Got another list to sort?" );
-
-
- - 4 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- gets( rsp )
- if ( rsp[0] != 'Y' )
- break;
- }
- }
- sort( numlist )
- char numlist[];
- {
- # bubble sort routine
- .
- .
- .
- }
-
-
- The first thing that strikes the BASIC programmer when
- he looks at a C program is the absence of line numbers! The
- C language relies purely on the location of statements
- within a program to determine the order of program
- execution.
-
- In the above example, notice the presence of the
- matching left and right curly braces ({ and }). These serve
- to bind together logical sections of the program. In
- particular, notice the first "{" (following "main()") and
- its partner towards the end of the program. These
- particular matching braces are used to "bind" everything
- between them to make one functional unit. This functional
- unit is called a "function" in C. Each function can be
- thought of as an autonomous entity - everything within the
- function is accessible only to statements within that
- function. The name of the function can be found immediately
- before the first "{", in this case, the function's name is
- "main". Another function can be found towards the end of the
- program, its name is "sort".
-
- So, in contrast to BASIC, a C program is a collection
- of these modular functions rather than just a sequential
- list of instructions.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 5 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 4. Functions
-
-
- Think of functions as a kind of "black box" machine;
- raw materials, in the form of information, goes into one end
- of the machine and a final product comes out of the other
- end. The inner workings of the machine are hidden and we
- don't really care to know how the machine works, as long as
- the final product is what we expected from the raw materials
- supplied.
-
- In C, the "raw materials" passed to a function are
- known as the function's "arguments" and the "final product"
- is called the function's "return value". C allows you to
- pass as many arguments to a function as needed, but the
- function always returns one and only one value. In the
- section on Variables we will see how a function can be made
- ____ to seem to return more than one value.
-
- To get SCI to execute the statements within a
- particular function, all you have to do is mention the
- function's name. In the program fragment shown above, you
- would type either "main()", or "sort()" at the SCI prompt.
- The parentheses following a function's name serve two
- purposes: they distinguish the entity as being the name of a
- function as opposed to a variable; and they show SCI where
- the function's arguments start and end. If a function does
- not require any arguments (as in "main()" above), you still
- need to supply the left and right parentheses. If a
- function requires more than one argument, each argument is
- seperated from the preceding one with a comma (,) like so:
-
-
- func( 23, 15, 34 )
-
-
- Note that the spaces are optional!
-
-
- 4.1. Library Functions
-
-
- Beyond using it as a rather dumb integer calculator,
- you can use the SCI shell to test out any valid C statement
- with support from a large collection of built-in functions.
- As you work through this tutorial, you will be introduced to
- many of these, refered to hereafter as "Library Functions"
- (see the section on Library Functions for more details).
- You may, if you like, think of the Library Functions as
- being analogous to BASIC's built-in commands like "PRINT"
- and "INPUT". Most of the Library Functions are similar to
- those shipped with "industrial strength" C compilers, so
-
-
- - 6 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- many of the programs you write under SCI should be
- transportable with some minor changes.
-
-
- 4.1.1. putd()
-
-
- The Library Function "putd()" prints a number, or the
- results of a calculation on the console screen. Try
- entering the following commands from the shell:
-
-
- putd(123)
- putd( 235 + 12370 )
-
-
- In the first example, the argument passed to "putd()"
- is the number 123. The function should have printed "123"
- on the console screen. In the second example, the argument
- is the sum of 235 and 12370. Note that this calculation is
- performed first, then the result is passed to "putd()" for
- printing.
-
- Below the numbers that were printed by "putd()" you
- should have seen a zero printed as well. This zero is the
- value returned by "putd()" and was printed by the shell. In
- this case, the return value of a function was not
- particularly useful. We were more interested in the
- side-effect of this function, namely the displaying of a
- number on the screen.
-
-
- 4.1.2. getchar()
-
-
- The Library Function "getchar()" waits for a single
- keyboard key to be pressed, then returns the value (in
- ASCII) of that key. At the shell prompt, try typing
- "getchar()", hit a carriage return and then hit the letter
- 'a' key. You should see the number 97 printed by the shell,
- the ASCII value in decimal of the character 'a'.
-
- Unlike "putd()", this function required no arguments
- and returned a useful value.
-
-
- 4.1.3. puts()
-
-
- The function "puts()" is used to print a sequence of
- characters (known in C jargon as a "string") on the
- console. A string is represented in C as a bunch of
-
-
- - 7 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- characters enclosed in quotes ("), just as in BASIC. Try the
- following command, and be careful to type the string exactly
- as it appears here:
-
-
- puts("hello, world\n")
-
-
- Look closely at the string again and notice the
- backslash (\) just before the letter 'n'. This two-character
- combination (\n) is standard C shorthand notation for a
- "newline" character. Newlines have the effect of performing
- a cariage return plus linefeed on the console. Had we
- omitted the "\n" from the string, "puts()" would have just
- printed "hello, world" and left the cursor on the same line,
- after the "d" in "world". SCI provides other similar
- shorthand notations, which will be explained in a later
- section.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 8 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 5. Your First Programs
-
-
- Now it's time to write your first program. If you
- haven't already done so, read the Editor section of the
- User's Manual and perform the installation as required for
- your particular computer. If you are unsuccessful in
- getting the editor to work properly, you can create the
- sample programs with your favorite text editor, then start
- up SCI and load the program file. This will be tedious and
- time consuming, but it may just give you enough
- understanding of C to perform the editor installation
- properly. If all else fails, appeal to the author for
- help!
-
-
- 5.1. Hello again, world!
-
-
- Either using the built-in editor or a seperate text
- editor, create the following program:
-
-
- hi()
- {
- puts("hello, world\n");
- }
-
-
- Now, from the shell, type the name of the function,
- "hi()". You should see the following on your screen:
-
-
- > hi()
- hello world
- 0
- >
-
-
- If instead you are rewarded with an error message
- followed by a question mark, you did something wrong! Hit a
- carriage return or two to get back to the shell's ">"
- prompt, go back into the editor, fix the mistake and try it
- again.
-
- Whether you realize it or not, this exercise is an
- important first step for learning a new programming
- language. It teaches you all of the routine motions you
- will be going through to write programs and gives you
- confidence to continue on.
-
-
-
-
- - 9 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 5.2. Fahrenheit to Celsius
-
-
- Next, type in the following sample program:
-
-
- fahr(celsius)
- {
- return 9 * celsius / 5 + 32;
- }
-
-
- This is a simple celsius to fahrenheit temperature
- conversion function. Notice here the symbols for
- multiplication (*) and division (/) are the same as in most
- other programming languages.
-
- Try executing this function with a few different
- celsius values. Each time the argument is converted to
- fahrenheit and is returned to the shell to be printed.
-
- As an exercise, modify the program to print the
- fahrenheit value and return a value of zero!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 10 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 6. Statements: Simple and Compound
-
-
- In C, a "statement" is just what you might expect; an
- imperative instruction to the computer to perform some
- calculation. Statements are generally some kind of
- arithmetic expression followed by a semicolon (or the end of
- line in SCI) - we have encountered them before. The C
- language also allows you to group together several of these
- "simple" statements and treat them as a single "compound"
- statement. This is done by placing left and right curly
- braces ({ and }) around the simple statements. Let's look
- at the example below:
-
-
- {puts("hello");puts("world\n");}
-
-
- Here, everything within the left and right braces and
- the braces themselves are treated as a single statement in
- C. The C language also lets us write the above statement
- like this:
-
-
- {
- puts( "hello" );
- puts( "world\n" );
- }
-
-
- Notice that the program becomes much easier to read
- when each statement is written on a seperate line. Also
- notice that we have indented the two simple statements from
- the braces. Indenting is the accepted way of conveying the
- intended structure of a program. We are in effect saying
- that these two lines "belong together" and should be treated
- as a single unit.
-
- The compound statement in the above example was
- obviously created for demonstration purposes only. If it
- had been encountered by itself in a real program, the braces
- would have been superfluous and would not have altered the
- behavior of the program. However, earlier we encountered an
- instance where the curly braces were required, namely
- immediately following a function definition. Later on when
- we discuss program flow control, we will again sing the
- praises of compound statements.
-
- We will now make just one more point concerning
- compound statements and the SCI shell. From the shell, type
- the following two statements:
-
-
-
- - 11 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- > puts("hello"); puts(" world");
- > {puts("hello"); puts(" world);}
-
-
- In the first instance, you saw that only the word
- "hello" was printed followed by the shell's ">" prompt.
- This is because the interpreter executes only the first
- statement it finds in the input line buffer. Since a
- statement is terminated by a semicolon, the second call to
- "puts" was never seen. In the second example, the
- interpreter saw the left curly brace, recognized the entire
- line as a single statement, and executed both calls to
- "puts()".
-
-
- 6.1. Comment Statements
-
-
- Comment statements are completely ignored by C and may
- be used liberally anywhere within a program for
- documentation purposes. Standard C uses the two-character
- combinations /* (pronounced "slash-star") and */ to mark the
- beginning and ending of comment statements:
-
-
- 2 + /* this is a comment */ 2 + 2;
-
-
- The /* and */ need not necessarily be on the same
- program line, as for example:
-
-
- 2 + 2 + 2;
- /*
- this is a comment
- */
-
-
- SCI uses the number symbol (#) to introduce comment
- statements. A comment in SCI begins with a # and ends at
- the end of the line. Being an interpreter, SCI required
- that comments appear on a single line, so only a comment
- start symbol was required. The above example might appear
- in SCI like this:
-
-
- 2 + 2 + 2;
- #
- # this is a comment
- #
-
-
-
-
- - 12 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- Be careful when placing comments because everything to
- the right of the first # symbol on the line is ignored by
- SCI.
-
-
- 2 + # this is a comment # 2 + 2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 13 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 7. Expressions
-
-
- Expressions can be thought of as components of a C
- statement - the values and operators that, when evaluated,
- yield a result. The most common example that comes to mind
- are arithmetic expressions:
-
-
- 2 + 3 - 5
-
-
- An expression becomes a statement if we simply tack a
- semicolon at the end of it, thus:
-
-
- 2 + 3 - 5;
-
-
-
- 7.1. Operators
-
-
- Since C provides a plethora of operators, we will not
- discuss them all in this section but rather introduce them
- as they become relavent to the discussion. If you have
- burning desire to discover all of C's operators, see the
- Appendix. First, we will define some commonly used terms.
-
-
- 7.1.1. Binary Operators
-
-
- The term "binary operator" does not refer to bits and
- bytes but rather to the class of operators that require two
- (hence "binary") operands. Some of these you have probably
- already seen if you are familiar with other programming
- languages, like the addition (+), subtraction (-),
- multiplication (*) and division (/) operators.
-
-
- 7.1.2. Unary Operators
-
-
- Unary operators perform their functions on only one
- operand. The subtraction symbol (-) is used as a unary
- operator when it stands in front of a number or a variable,
- like so:
-
-
- -45
-
-
-
- - 14 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- You may also use the plus sign (+) as a unary operator,
- although it would be superfluous since all numbers are
- assumed to be positive unless preceeded by a minus sign. C
- also provides other unary operators that will be discussed
- later.
-
-
- 7.2. Precedence
-
-
- If you will recall, in your high school algebra class
- you learned that in an arithmetic expression containing a
- combination of addition, subtraction, division and
- multiplication, the division and multiplication are always
- done before addition and subtraction. That is to say that
- division and multiplication "take precedence" over addition
- and subtraction. This property of precedence extends to all
- operators in the C language, not just the arithmetic
- operators.
-
- You may defeat the normal order of evaluation of an
- expression by using parentheses, just as in modern algebra:
-
-
- (2 + 3) * 5
-
-
- This will perform the addition first, then the
- multiplication. You may use as many matched sets of
- parentheses as necessary to disambiguate the order of
- evaluation:
-
-
- ( ( (2+3) / 2 ) * 5 )
-
-
- In fact, it is a good idea to use parentheses liberally
- whenever you are unsure of operator precedence.
-
-
- 7.3. Associativity
-
-
- You also learned (hopefully in the same algebra class)
- that expressions are always evaluated from left to right.
- This same rule applys to expressions in C. This property of
- operators is known as associativity. In C, most of the
- ____ ____ __ _____ binary operators are evaluated from left to right, while the
- _____ __ ____ unary operators are evaluated from right to left.
-
-
-
-
-
- - 15 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 7.4. Arithmetic operators
-
-
- Now we are finally prepared to formally introduce C's
- arithmetic operators. They are listed here in order of
- decreasing precedence:
-
-
- * / % multiplication, division and modulo
- + - addition and subtraction
-
-
- Most of these should already be familiar to you. The
- modulo operator (%) gives the remainder from the division of
- the left value by the right value. For example, the result
- of: 15 % 8 is 7.
-
-
- 7.5. Bitwise Operators
-
-
- C also offers these bit-manipulation operators (again
- listed in decreasing precedence):
-
-
- << >> left and right SHIFT
- & bitwise AND
- ^ bitwise exclusive OR
- | bitwise OR
-
-
- If you have a need to do bit manipulation but are not
- familiar with the above terms (SHIFT, AND, OR and exclusive
- OR), you should probably consult a textbook on computer
- programming since this is beyond the scope of this
- tutorial.
-
- We will be learning more about other C operators in
- later discussions.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 16 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 8. Variables
-
-
- Previously, we had only eluded to the fact that C does
- allow you to create named data storage locations (a.k.a.
- "variables"), now we will formally introduce you to all of
- C's data types.
-
- Except for the pre-defined Library Functions and the
- editor's system-variables (which are found in SHELL.SCI),
- all variables must first be made known to the program before
- they may be used. Unlike BASIC where a variable comes into
- existance the very first time it is used in a statement, C
- requires that every variable be formally declared before you
- may use it within your program. This section will cover the
- fundamentals of C variable declarations.
-
-
- 8.1. Naming Conventions
-
-
- The precise rules governing the naming of variables
- usually varies from one C compiler to another. The rules
- for SCI variable names are as follows:
-
-
- 1) a variable name may contain any number of characters
- from the set of:
-
-
- 1) the letters "a" through "z" and "A"
- through "Z".
-
- 2) the underscore (_).
-
- 3) the digits "0" through "9".
-
-
- 2) the first character of a variable must not be a
- digit (i.e. it must be either a letter or an
- underscore).
-
- 3) the case of a letter is significant, for example:
- "foobar" is not the same as "Foobar" or "FooBar".
-
- 4) although a variable may be as long as you like, only
- the first eight characters are signifcant. For
- example: "return_value" and "return_value_1" refer
- to the same variable, since only the first eight
- characters ("return_v") are significant.
-
-
-
-
- - 17 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 8.2. Data Types
-
-
- The C language supports many different types of
- variables. The most notable difference between them is the
- amount of memory storage each one addresses. The least
- amount of memory a variable can represent depends on the
- type of computer the program is written for. Typically,
- this is a byte of information, although some mainframe
- machines do not have the capability to access memory in
- smaller than 2 or 4 byte gobbles. Most personal computers,
- however can access memory one byte at a time and in C, this
- data type is known as the "char", short for "character".
-
-
- 8.2.1. Char
-
-
- A "char" variable in SCI is one byte long and can
- represent a number between -128 and +127. In order to make
- a variable known to the program we must first declare it, so
- to declare a "char" variable named "foobar" we would write:
-
-
- char foobar;
-
-
- We can also declare more than one variable of the same
- type on the same line by seperating each with a comma, like
- so:
-
-
- char foobar, snafu, gurgle;
-
-
-
- 8.2.2. Int
-
-
- Another variable type is the "int", short for
- "integer". Again, the amount of memory an "int" addresses is
- machine dependent. In SCI, an "int" addresses two bytes of
- memory, and can represent a number between -32768 and
- +32767. "Int"s are declared in a manner similar to "char"s:
-
-
- int foobar;
- int snafu, wowbagger;
-
- Standard C also defines other data types such as floating
- point variables, double precision integer and double
- precision floating point. You may also define your own data
-
-
- - 18 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- types that are a combination of these primaries (known as
- "structures"). Unfortunately, these are all not supported
- by this version of SCI.
-
-
- 8.3. Scope
-
-
- If you are familiar with BASIC, then you already know
- that a BASIC program variable is "known" throughout the
- program - that is, any statement within the program may
- alter a variable's contents. This "feature" can lead to
- some very difficult to find programming bugs. For instance,
- you may use a variable as a temporary loop counter in one
- section of the program, only to discover later that you had
- already decided to use that variable for another purpose and
- its contents were continually being destroyed. Ideally, we
- would like to be able to use variable names indiscriminantly
- in one section of a program without having to worry about
- whether the variable name is being used in another section
- of the program. Happily, the C language offers this ability
- as you will soon see. This concept of limited (or rather
- "controlled") access to variables is known as "scope".
-
-
- 8.3.1. Global Variables
-
-
- In C, you may create variables that are known
- throughout the program, just like in BASIC. Variables that
- have this property are known as "globals" and just like
- BASIC, every statement within the program may retrieve and
- store the value of a global variable. A variable will
- attain global status if it was declared outside of any curly
- braces ({ and }) that delimit the body of a function. Here
- is an example to illustrate:
-
-
- char c; # "c" is a global
- int i, j; # and so are "i" and "j"
-
- a_function() # the first function in the program
- {
- .
- .
- .
- }
-
- char flag, nyuk; # some more global variables
-
- another_function() # another function
- {
-
-
- - 19 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- .
- .
- .
- }
-
-
- As you can see, C does not care where within a program
- a global variable is declared as long as the declaration
- appears outside of any functions.
-
- SCI ensures that global variables are always set to
- zero before the program starts up. This is pretty much
- standard behavior for most C compilers as well.
-
- In C, functions are also considered to be globals -
- they are known throughout the program, although they
- obviously can't be used to store data.
-
-
- 8.3.2. Local Variables
-
-
- Variables that are declared inside of the curly braces
- that mark the beginning and end of a function are known as
- "local" variables. Locals exist only during the life of the
- function - that is the variable comes into existence after
- it has been declared within a function and ceases to exist
- when the function returns to its caller. See the example
- below for clarification:
-
-
- char c; # these are global variables
- int i;
- a_function() # a function definition
- {
- char snafu; # a local variable
- int x, y; # some more locals
- .
- .
- .
- x = c; # copy the global to a local
- } # snafu, x and y cease to exist!
-
-
- ____ Variable declarations must appear immediately after a
- left curly brace; if a declaration appears anywhere else
- within the body of a function SCI will warn you about a
- "syntax error".
-
- ___ In addition, variables may be declared within any
- ____ compound statement in a function, but the declarations must
- appear immediately after the opening brace. Variables
-
-
- - 20 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- declared in this context exist only for the life of the
- compound statement, i.e. to the matching closing brace.
- Thus the memory these variables occupy can be re-used within
- the function. Below is an example to illustrate:
-
-
- func()
- {
- char a;
- int i;
- .
- .
- .
- if ( i==0 )
- {
- int j; # declare an "int" in a compound stmt
- .
- . # "a", "i" and "j" are all locals here
- .
- } # "j" no longer exists here
- else if ( i==1 )
- {
- char j; # a different "j" than above
- .
- .
- .
- }
- }
-
-
- SCI ensures that locals are always zero just after they
- have been declared. On standard C compilers, the initial
- contents of locals is unknown, so do not depend on them
- being zero.
-
-
- 8.3.3. Function Arguments
-
-
- Function arguments are also considered to be local
- variables. When a function calls another function and
- passes it an argument, the argument's contents is copied
- into a local variable in the called function - the value of
- the caller's argument is not affected. This is best
- illustrated with an example:
-
-
- char c; # the global variable, "c"
- func1()
- {
- char c; # a local may have the same name as a global
- c = 1; # and this sets the LOCAL variable "c" to 1!
-
-
- - 21 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- func2(c); # now call func2
- }
- func2( x )
- char x;
- {
- char c; # this "c" is different from func1's "c"
- x = 3; # this does not affect func1's "c"
- c = 5; # this does not affect the global "c"
- }
-
-
-
- 8.3.4. System Globals
-
-
- As mentioned earlier, the Library Functions and the
- editor's configuration variables that are declared in the
- shell are also globals. These however, are more permanent
- than program globals. A program's global variables can be
- zapped into non-existence simply by editing the program and
- removing the statement that declares them. System globals
- can not be destroyed since SCI will not allow you to modify
- the shell program (or any program for that matter) while it
- is still running.
-
-
- 8.4. Location of Variables
-
-
- At this point it may be useful to discuss where in
- memory each of these different types of variables is
- located. Although this depends on the compiler's
- implementation and the hardware, most C compilers take
- advantage of some commonly used data structures.
-
-
- 8.4.1. The Stack
-
-
- The stack is simply a chunk of the computer's memory
- that can only be accessed (read from and written to)
- indirectly through a machine register known as the "stack
- pointer". If the CPU does can not provide a stack pointer
- register, the authors of the C compiler will typically write
- some subroutines in the machine's language to emulate a
- hardware stack. Reading and writing to the stack proceeds
- as follows: before an item is read from the stack, the stack
- pointer is decremented to point to the previous item in the
- stack memory. This then, is the item read from the stack;
- After an item is written into stack memory, the stack
- pointer is incremented to point to the next item in the
- stack. Thus, the operation of the stack can be thought of
-
-
- - 22 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- as a stack of pancakes - numbers are piled onto the stack
- for temporary storage, then removed from the top as needed.
-
- In general, the C language depends very heavily on the
- stack. Local variables, including function arguments, are
- piled onto the stack when a function begins, and then
- removed and discarded when it terminates. As a C statement
- is executed, the components of the statement (constants,
- variables, etc.) are "pushed" onto the stack until they are
- needed. Then, when the statement is evaluated, the
- components are "poped" off the stack.
-
- Most compilers take advantage of the machine's built-in
- stack (if the CPU happens to have one, as most do), so
- access to the stack is very efficient. Still, this has
- become a major point of criticism by opponents of the C
- language.
-
-
- 8.4.2. Program and Data Segments
-
-
- Global data variables are usually stored in the same
- section of memory as program code; most 8 and 16 bit CPU's
- do not provide seperate memory segments for program code and
- global data.
-
- Some minicomputers and most mainframes do provide
- seperate program code and data memory areas. The machine
- then limits access to these segments by disabling the
- program from storing data in the code segment and possibly
- causing the program to go berserk. Also, the program is
- limited to accessing only its own global data area and
- attempts to read or write data outside of this global data
- segment is a violation.
-
- Alas, a microcomputer's operating system is at the
- mercy of the currently executing program and a careless
- program has the ability to corrupt the operating system and
- bring the computer to its knees.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 23 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 9. Constants
-
-
- You already know about decimal integer constants
- because we have been using them throughout this tutorial.
- The C language also allows you to represent numbers in
- hexadecimal, octal and ASCII.
-
-
- 9.1. Hexadecimal Constants
-
-
- Hexadecimal numbers are distinguished from other number
- representations and variables by preceding them with a "0x"
- (zero-"ex"), for example:
-
-
- 0x0
- 0x1b
- 0xfa70
-
-
- are all valid hexadecimal number representations. You
- may also use an upper case "x" if you desire.
-
-
- 9.2. Octal Constants
-
-
- Octal numbers are distinguished by preceding them with
- a zero. These are all valid octal numbers:
-
-
- 00
- 033
- 0175160
-
-
-
-
- 9.3. ASCII Character Constants
-
-
- The numeric value of ASCII characters can be
- represented by surrounding the ASCII character in
- apostrophes, like this:
-
-
- A 'A' - is equivalent to decimal 65
-
- ' ' - is a space and is equivalent to decimal 32
-
-
-
- - 24 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- Certain non-printing ASCII characters can also be
- conveniently represented as character constants. By
- preceeding certain lower case letters with a backslash
- character ("\"), the two-character combination can be used
- to represent a single one byte value. One of these you
- already know as the "newline" character, '\n'. Here is a
- complete list of these:
-
-
- b '\b' - "backspace", equivalent to decimal 8.
-
- r '\r' - "return", equivalent to 13.
-
- n '\n' - "newline", equivalent to 10.
-
- f '\f' - "formfeed", equivalent to 12.
-
- t '\t' - "tab", equivalent to 9.
-
-
- In addition, you can represent any ASCII character as a
- character constant using its octal equivalent preceded by a
- backslash. The only restriction here is that the octal
- representation must be exactly 3 octal digits. For example:
-
-
- 033 '\033' - is an ASCII "escape" character equivalent to
- 27.
-
- 101 '\101' - is an ASCII "A", equivalent to 'A', equivalent
- to 65.
-
- 377 '\377' - is not really a valid ASCII character, but it
- is equivalent to 255.
-
-
- and so on - you get the idea.
-
-
- 9.4. String Constants
-
-
- Finally, another type of constant you have already been
- using, is the "string" constant - a bunch of ASCII
- characters surrounded by quotes, for example:
-
-
- "this is a string\n"
-
-
- A string always ends with a zero byte, thus the amount
- of memory a string takes up is equal to the number of
-
-
- - 25 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- characters you can count in the string plus one. In the
- example above, the string requires 18 bytes of storage
- (realize that the "\n" sequence is a single character - the
- "newline"!).
-
- String constants have an interesting numeric equivalent
- - it is an address in the computer's memory where the ASCII
- characters in the string can be found by functions that are
- equiped to deal with them. For instance, the Library
- Function "puts" expects its parameter to be an address in
- memory where ASCII character can be found and sequentially
- printed out to the console screen.
-
- If you tried to find out a string constant's numeric
- value from the shell by typing:
-
-
- > "hello?"
- -5537
- > "another string..."
- -5537
- > "what the?"
- -5537
- >
-
-
- you would be surprised to find that they all have the
- same address - how could this be? Actually, all the string
- constants in the above examples do have the same address.
- Recall that the shell reads a line of input from the console
- and hands it off to the interpreter for evaluation. Since
- the strings all get read into the same line buffer by the
- shell, they all have the same address, namely the shell's
- input line buffer.
-
- We will discuss strings in more detail in the section
- on arrays and pointers.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 26 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 10. Assignment Operator
-
-
- In C, we assign values to variables using the
- "assignment" operator, "=". Do not confuse the assignment
- operator (a single equal sign) with the "is equal to"
- relational operator (two consecutive equal signs), which we
- will discuss later. Although C will allow you to do this
- under certain conditions, you will get unexpected results.
- The expression:
-
-
- a = (b + 1) * 2;
-
- is read as: take the results of the calculation of (b + 1) *
- 2 and assign it to the variable "a". Note that there may be
- only one variable to the left of the equal sign.
-
- You can if you like, string several of these
- assignments together like this:
-
-
- a = flg = x = (b + 1) * 2;
-
-
- Note that even here there is always only one variable
- to the left of each equal sign. This statement is evaluated
- like so: take the results of the calculation of (b + 1) * 2
- and assign it to the variable "x", then assign the same
- number to the variable "flg", and then to "a". This implies
- that the assignment operator is evaluated from
- _____ __ ____ right-to-left, instead of the usual left-to-right. In fact
- it is the only binary operator supported by SCI that
- exhibits this peculiar behavior. This feature is most
- useful when initializing several variables, like so:
-
-
- lettercnt = digitcnt = punctcnt = 0;
-
-
- which would set all of the variables to zero.
-
- The assignment operator has the lowest precedence (it
- is performed last in an expression) of all the C operators,
- except for the "comma" operator (see below).
-
-
- 10.1. Lvalues and Rvalues
-
-
- It should be intuitively obvious that any attempt to
- store a value in what we know as a C constant is illegal.
-
-
- - 27 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- In other words, you would never attempt to say, store the
- number 3 in place of the number 5:
-
-
- 5 = 3;
-
-
- The same holds true for string constants; you may not
- store another string in an existing string constant:
-
-
- "hello" = "world\n";
-
-
- These types of data (constants) are collectively known
- as "rvalues" (pronounced "are-values"). The term rvalue
- stems from the fact that they may only be used on the
- right-hand-side of an assignment operator.
-
- On the other hand, variables do allow numbers to be
- stored and retrieved from them. This category of data is
- known as "lvalues" (pronounced "ell-values") because they
- may be used on the left-hand-side of an eual sign.
-
- We will encounter lvalues and rvalues again in a later
- discussion.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 28 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 11. Comma Operator
-
-
- In C the punctuation character "," (comma) is
- considered to be an operator, although it does nothing more
- except insure that sub-expressions within a statement will
- be evaluated in order from left to right. This operator has
- the lowest priority of all. It is useful for when you want
- to do more than one thing in a statement, like the
- following:
-
-
- ++a, b=12, c=b+a;
-
-
- Of course, we could also have written the above
- statement as:
-
-
- {
- ++a;
- b=12;
- c=b+a;
- }
-
-
- but this would not have been as concise as the first form.
-
- Commas are also used to seperate variable names in a
- data declaration as you have already seen, and to seperate
- arguments in function calls.
-
-
- NOTE NOTE:
-
- SCI does not support the use of the comma operator
- anywhere outside the context of variable
- seperators or function call argument seperators,
- as in the first example above. Any attempt to do
- so will result in a "syntax error" message from
- the interpreter.
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 29 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 12. Flow Control
-
-
- The previous sections have dealt only with data
- elements (variables and constants) and with evaluating
- arithmetic combinations of these. C would be a poor
- language indeed if it only allowed a programmer to evaluate
- a sequential list of arithmetic expressions without giving
- him the opportunity to act on the results of these
- calculations. This section will introduce you to C's
- program control structures, also known as "flow control"
- structures. Most of these have constructs that should be
- familiar to all you BASIC hackers: the conditional
- ("if-else"), looping ("while" and "for") and program control
- switching ("switch").
-
-
- 12.1. if and if-else
-
-
- The most fundamental of the flow control constructs is
- the "if". This allows you to perform a statement (or group
- of statements if we talk about a compound statement) "if"
- the given condition is true. In C, a condition is
- considered to be true when the value of an expression is
- non-zero, that is either a positive or negative number. It
- follows then, that an expression that evaluates to zero is
- considered to be a false condition. We write an "if"
- conditional in C like this:
-
-
- if ( <expression> ) <statement>
-
-
- We will be using the angle brackets (<>) to represent
- familiar C concepts so that you will be able to more easily
- identify the relavent components: here "<expression>" is any
- valid C expression, like "var-5" or "x + 10"; and
- "<statement>" may be either a simple or compound statement -
- but, more about statements later.
-
- The relavent components in the "if" statement are:
- obviously the "if" word which identifies this flow control
- construct; a left parenthesis followed by an expression
- followed by a right parenthesis; then a C statement. Now a
- few words about syntactics:
-
-
- 1) The "if" must be in lower case letters, most C
- compilers will usually not accept "If", or "IF" or
- "iF" (and neither will SCI!).
-
-
-
- - 30 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 2) The matched left and right parentheses must be
- included, and SCI requires that the "if", the left
- parenthesis, the <expression> and the right
- parenthesis appear on the same line in the
- program.
-
- 3) The <statement> may be either a simple statement or
- a compound statement.
-
-
-
- NOTE NOTE:
-
- SCI requires that the "if" and the parentheses
- appear on the same line in your program text, but
- the <statement> may appear on the following line.
- This is only a restriction of the SCI interpreter
- - the standard C language lets you put as much
- horizontal and vertical "distance" between
- elements of an "if" statement as you like.
-
-
- The "if" flow control construct behaves as follows:
-
-
- 1) The <expression> is evaluated.
-
- 2) If the result of <expression> is true (a non-zero
- value) then the <statement> is executed.
-
- 3) If the result is false (zero) the <statement> is
- skipped and program control passes to the next
- statement.
-
-
- For example:
-
-
- if ( 2 + 2 ) a = 5;
-
-
- would always set the variable "a" to 5 because the
- <expression>, which evaluates to 4 in this case, is always
- non-zero. And the <statement> in this example:
-
-
- if ( 0 ) a = 5;
-
-
- would never be reached because the <expression> is always
- false. As a last example, look at this:
-
-
-
- - 31 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- if ( a = b + c ) b = b + 1;
-
-
- Here the value of the <expression> depends on the
- results of the addition of "b" and "c", which we have no way
- of knowing just by looking at the example out of context.
- As a side-effect, the result of the addition is stored in
- the variable "a". It is very important that you realize that
- the equal sign in "a = b + c" is not making a comparison
- between the value of "a" and "b + c", as you might assume if
- you were looking at a similar statement in BASIC. In other
- ___ words, we are not saying "if a is equal to the sum of b and
- c".
-
-
- 12.1.1. More About Statements
-
-
- Earlier we promised to tell you more about the concept
- of C statements. A statement, as you already know, can be
- either an expression such as "a = 5 + 5" followed by a
- semicolon (don't forget the semicolon!) or it may be a group
- of these simple statements surrounded by left and right
- curly braces, like this:
-
-
- { a = b + c; b = b + 1; }
-
-
- or this:
-
-
- {
- a = b + c;
- b = b + 1;
- }
-
-
- or a compound statement within a compound statement as in
- this example:
-
-
- {
- { a = b; b = 1; }
- a = b + c;
- b = b + 1;
- }
-
-
- We now expand our definition of a C statement to
- include the "if" and later all of the other flow control
- constructs as well. In other words, the template for an
-
-
- - 32 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- "if" we showed you before:
-
-
- if ( <expression> ) <statment>
-
-
- can be thought of as a single unit and used wherever a
- <statement> is used. Now we can write multiple "nested
- if's" like this:
-
-
- if ( a + 5 )
- if ( b - 1 )
- c = 0;
-
-
- which would be "read" by the computer as follows:
-
-
- 1) if "a + 5" is true (non-zero) then go to step 2
- otherwise go to step 3.
-
- 2) if "b - 1" is true, then assign 0 to "c".
-
- 3) go on to the next statement in the program.
-
-
- The "if" statement also has an optional "else" clause,
- which looks like this:
-
-
- if ( <expression> ) <statement> else <statement>
-
-
- Notice that the "else" keyword must be in lower case
- letters also. Again, the first <statement>, the "else", and
- the second <statement> may be on seperate lines of program
- text or they may all be on the same line. The "if-else"
- statement, as it is called, is read as follows:
-
-
- 1) if the <expression> is true, then execute the first
- <statement> and then go to step 3.
-
- 2) else, execute the second <statement> and then go to
- step 3.
-
- 3) go on to the next statement in the program.
-
-
- The C language allows you to nest "if-else" statements
- as deeply as you wish, for example:
-
-
- - 33 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- if ( a + 3 )
- if ( b + 5 )
- if ( c + 7 )
- d = 0;
- else
- d = 1;
- else
- d = 2;
- else
- d = 3;
-
-
- It should be obvious from the way the statements were
- indented how each "else" matches its "if". As a matter of
- definition, an "else" clause matches the nearest preceding
- "if" clause. If there are more "else's" than "if's" in a
- program, then this is an error condition and you will be
- warned by SCI. If you are ever unsure how nested "if-else"
- combinations will match up, you can always use curly braces
- to bind them together the way you want:
-
-
- if ( a + 3 )
- {
- if ( b + 5 )
- {
- if ( c + 7 )
- d = 0;
- else
- d = 1;
- }
- else
- {
- d = 2;
- }
- }
- else
- {
- d = 3;
- }
-
-
- You can also use another "if-else" statement in the
- "else" clause of a preceding "if-else", for example:
-
-
-
-
-
-
-
-
-
-
- - 34 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- if ( a + 3 )
- d = 1;
- else
- {
- if ( a + 4 )
- d = 2;
- else
- {
- if ( a + 5 )
- d = 3;
- else
- d = 4;
- }
- }
-
-
- Because it is a matter of personal style, there are no
- hard and fast rules to follow when indenting program
- statements like this. However, the above example is more
- commonly indented like this:
-
-
- if ( a + 3 )
- d = 1;
- else if ( a + 4 )
- d = 2;
- else if ( a + 5 )
- d = 3;
- else
- d = 4;
-
-
- This saves you from running off the right edge of the
- screen when writing very deeply nested "if-else's" and it
- looks very much like a multi-path switch (an "ON GOTO"
- statement in BASIC).
-
-
- 12.1.2. Relational Operators
-
-
- Sometimes it is necessary to change the program flow
- depending on whether a variable is equal to a certain
- value. The C language has this ability to test equality of
- two expressions using a set of operators (similar to the
- addition, multiplication, assignment, etc.) known as the
- "relational operators". If we wanted to know if a variable
- were equal to a certain value, for instance we could say:
-
-
- a == 5
-
-
-
- - 35 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- The "==" (which is read as: "is equal to") operator
- compares the two items to the left and right of it and
- leaves a value of one if they are equal, zero if they are
- unequal. So the value of this expression would be one if
- "a" is equal to 5 and zero otherwise. Thus, the statement:
-
-
- if ( a == 5 )
- b = 0;
-
-
- would set "b" to zero only if "a" equals 5.
-
- We promised you a set of these operators, so here they
- are:
-
-
- ________ ____ __ operator: read as:
-
- == is equal to
- != is not equal to
- < is less than
- > is greater than
- <= is less than or equal to
- >= is greater than or equal to
-
-
- Notice that there may be no spaces between the two
- equal signs (=) in the "is equal to" operator nor between
- the exclamation point (!) and the equal in the "is not equal
- to". If there is a space between them, they will be assumed
- to be two seperate operators and you will get a "syntax
- error" message from SCI. Needless to say, the same goes for
- the <= and >=.
-
-
- 12.1.3. Logical Operators
-
-
- The C language also allows you to combine groups of
- relational expressions with "and" and "or" clauses. For
- example, given two sets of conditions you can determine if
- both are true, or if at least one is true. These "and" and
- "or" clauses are known as the "logical operators" in C:
-
-
- ________ ____ __ operator: read as:
-
- && and
- || or
-
-
-
-
- - 36 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- Notice that there may be no spaces between the two
- ampersands (&) and vertical bars (|). The expression:
-
-
- a==5 && b==3
-
-
- ___ will be true only if "a" is equal to 5 and "b" is equal to
- 3.
-
-
- a<0 || 10<a
-
-
- In this example the expression is true if "a" is less
- __ than 0 or greater than 10 (do you see that 10<a and a>10 are
- identical?).
-
- One final note about the logical operators: standard C
- stops evaluating an expression that contains logical
- operators after the truth or falsehood of the expression is
- known. For example:
-
-
- a==b && c==d
-
-
- Assume that "a" is not equal to "b". Standard C would
- not even bother checking the relation "c==d" because the
- "and" clause requires both expressions to the left and right
- of the "&&" to be true - if one of the components is false,
- the entire expression is false. So, since the first
- component encountered ("a==b") was found to be false, the
- truth or falsehood of the entire expression is already known
- and there is no need to evaluate "c==d".
-
-
- NOTE NOTE:
-
- SCI is not as smart as a standard C compiler and
- blindly evaluates every sub-expression in a
- logical expression. This can lead to some very
- hard to find errors if for example you alter a
- variable in one of the subexpressions of a logical
- operation - sorry folks!
-
-
-
- 12.1.4. Precedence and Associativity
-
-
- You can combine as many "and" and "or" clauses as
-
-
- - 37 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- necessary:
-
-
- a==5 || b==3 && c==4
-
-
- This expression will be true under one of two
- conditions: 1) b is equal to 3 AND c is equal to 4, or 2) a
- is equal to 5. This example shows a very important property
- of logical operators that we have already encountered in the
- discussion of the arithmetic operators (+, -, *, etc.),
- namely precedence. In C, the && operator takes precedence
- (is performed before) the || operator.
-
- As with the arithmetic operators the logical operators
- are performed from left to right. So, if we have more than
- one operator of the same precedence in an expression:
-
-
- a==5 && b==5 && c==5
-
-
- we know that C performs the tests for equality from left to
- right.
-
- Although we haven't come right out and said it before,
- it should be obvious from the examples that the relational
- operators have higher precedence than the logical
- operators. Specifically, >, >=, < and <= have higher
- precedence than == and != which have higher precedence than
- && which has a higher precedence than ||. Please refer to
- the Language Summary section of the User's Manual for a
- complete list of C operators and their order of precedence.
-
- Furthermore, relational operators associate from left
- to right, although it is hardly ever necessary to use more
- than one consecutive relational:
-
-
- a == 5 != 1
-
-
- Notice in this example that "a == 5" is performed first
- which would result in either a zero or a one. Examine the
- following expression closely:
-
-
- a < 5 == b < 5
-
-
- ____ This expresion will be true if "a" and "b" are both
- ____ ____ _ ____ _____ __ __ _______ ____ less than 5 or "a" and "b" are both equal to or greater than
-
-
- - 38 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- _ 5.
-
- As always, whenever you are in doubt about the
- associativity or precedence of operators, either use
- parentheses to bind operands and operators together, or
- consult the table of operators in the User's Manual.
-
-
- 12.1.5. Examples
-
-
- Finally armed with these new facts about C, we are
- ready to try some practical examples using the SCI
- interpreter. Enter the following program using the SCI
- editor:
-
-
- convert(n)
- {
- char c;
-
- puts("to decimal (d), hex (x) or octal (o) ?");
- c=getchar();
- if(c=='d')
- putd(n);
- else if(c=='x')
- putx(n);
- else if(c=='o')
- puto(n);
- else
- puts("what?\n");
- }
-
-
- As you can probably tell, this is a number conversion
- routine. It asks the operator whether to convert the number
- passed to it ("n") to decimal, hexadecimal or octal. Now at
- the shell prompt, type:
-
-
- > convert( 255 )
- to decimal (d), hex (x) or octal (o) ?x
- 0xff
- 0
- >
-
-
- If your screen did not look like the above, there is
- something wrong - fix it up and try again.
-
- Now modify the program to accept either lower or upper
- case d's, x's and o's (hint: use the || operator).
-
-
- - 39 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 12.2. while
-
-
- One of the things that makes a computer such a powerful
- tool is its tireless ability to perform repetitive tasks.
- This is why every programming language has some sort of
- "looping" flow control. The C language offers three types
- of loop constucts: the "while", "for" and "do-while". This
- version of SCI only supports the "while" and "for".
-
- The "while" flow control looks something like this:
-
-
- while ( <expression> ) <statement>
-
-
- Notice that its structure is very similar to the "if"
- construct and all of the syntactical rules apply as well.
- This statement is executed as follows:
-
-
- 1) evaluate <expression> and if it is false, go to step
- 4.
-
- 2) execute <statement>.
-
- 3) go to step 1
-
- 4) go on to the next statement in the program.
-
-
- As with the "if" statement, <expression> is considered
- to be true if it evaluates to non-zero, and false if it is
- zero. Let's look at an example:
-
-
- loop()
- {
- int a;
-
- a = 10;
- while ( a > 0 )
- {
- putd( a );
- a = a - 1;
- }
- puts("all done\n");
- }
-
-
- This loop will get executed exactly 10 times - each
- time the variable "a" is decremented by one until it equals
-
-
- - 40 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- zero. When "a" reaches zero, the expression "a > 0" will
- become false and program execution will continue with the
- next statement in the program.
-
- There is also a more direct method of breaking out of a
- "while" loop without having to wait until control returns
- back to the <expression> evaluation and testing. Using a
- "break" statement, you can directly jump out of a "while"
- and continue with the next statement in the program. The
- following demonstrates the use of a "break":
-
-
- loop()
- {
- int i, r;
-
- i = 13560;
- putd( i );
- while ( 1 )
- {
- r = i / 10;
- if ( r == 0 )
- break;
- putd( r );
- i = r;
- }
- puts("all done\n");
- }
-
-
- Since the <expression> is always true (1 is always
- non-zero), this loop would be repeated until the cows came
- home. The "if" statement within the loop will break out of
- the loop when the remainder of the division results in
- zero. We leave as an exercise for the student to figure out
- what this little program does.
-
-
- 12.3. for
-
-
- The "for" looping construct is similar to the "while".
- Its format is as follows:
-
-
- for ( <expression> ; <expression> ; <expression> ) <statement>
-
-
- The syntactical requirements of the "for" construct are
- similiar to those of the "while" - the "for" and the
- parentheses and everything between them must be on the same
- program line. Also, the three <expression>'s inside the
-
-
- - 41 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- parentheses must be seperated from each other by two
- semicolons as shown.
-
- Actually, the "for" is simply a method of clearly
- presenting to the reader the most commonly needed elements
- relavent to a program loop: an "initialization" part, a
- "loop test" part and an "iteration" part. These three
- elements are clearly identifyable, and correspond to
- (reading from left to right) the three <expressions> within
- the parentheses.
-
- A "for" statement would be executed as follows:
-
-
- 1) evaluate the first <expression>, disregard the
- result.
-
- 2) evaluate the second <expression> and if it is false,
- go to step 5.
-
- 3) execute <statement>.
-
- 4) evaluate the third <expression> and go to step 2.
-
- 5) go on to the next statement in the program.
-
-
- We could have written the first example given for the
- "while" using a "for" statement:
-
-
- loop()
- {
- int a;
-
- for ( a = 10; a > 0; a = a - 1 )
- putd( a );
- }
-
-
- The "break" statement may also be used to break out of
- a "for" loop. One last interesting feature of the "for" is
- that any or all of the three <expressions> may be missing.
- If the second <expression> is missing, the "loop test" will
- always evaluate to true. Thus, the second example of the
- "while" loop above could have been written:
-
-
-
-
-
-
-
-
- - 42 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- loop()
- {
- int i, r;
-
- for ( putd( i = 13560 ); ; i = r )
- {
- r = i / 10;
- if ( r == 0 )
- break;
- putd( r );
- }
- }
-
-
- Here, the three <expressions> are:
-
-
- putd( i = 12560 )
-
- missing!
-
- i = r
-
-
- And the most efficient way of writing a "forever" loop
- is:
-
-
- for ( ;; )
- puts( "hello, world\n" );
-
-
-
-
- 12.4. switch
-
-
- The last and most complex flow control we will examine
- is the multi-path "switch" statement. The "switch" is
- similar to BASIC's "ON GOTO" statement. Here is the
- template of a "switch" statement:
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 43 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- switch ( <expression> )
- {
- case <constant expression> : <statement>
- case <constant expression> : <statement>
- .
- .
- .
- case <constant expression> : <statement>
- default : <statement>
- }
-
-
- Again, the word "switch", the left parenthesis, the
- <expression> and the right parenthesis must be on the same
- program source line. The matching left and right curly
- brace at the beginning and end of the switch are not
- actually required but are necessary as you will soon see.
- The words "case" and "default" are only meaningful within
- the context of a "switch" statement. There may be any
- number of "case <constant expression> :" sequences but only
- one "default :". The <constant expression>'s are simply
- <expression>'s that contain only constants (no variables!).
- So, the following would all be examples of <constant
- expression>'s:
-
-
- 2 + 2
- 25 * (365 / 7)
- 37/12 > 10
-
-
-
- NOTE NOTE:
-
- Standard C requires that only <constant
- expression>'s follow a "case", however SCI allows
- you to use any valid <expression> as an added
- bonus. Keep this fact in mind when writing
- programs that will eventually be transported to
- standard C!
-
-
- The "switch" statement behaves as follows:
-
-
- 1) evaluate the <expression>.
-
- 2) compare the results of <expression> to each of the
- <constant expression>'s after the "case's"
- sequentially from top to bottom.
-
- 3) if the value of <expression> matches one of the
-
-
- - 44 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- <constant expression>'s, continue program
- execution with the statement immediately following
- the colon. All other "case" and <constant
- expressions> are ignored.
-
- 4) if none of the <constant expression>'s match
- <expression>, jump to the <statement> immediately
- following the word "default"
-
- 5) if a "break" statement is encountered, jump to the
- end of the "switch" statement (the <statement>
- immediately following the }).
-
-
- Although this seems complicated at first, a "switch" is
- really just a multi-way program jump. It allows you to jump
- to anywhere within a statement, based on the value of an
- expression.
-
- Here's an example of a switch:
-
-
- convert(n)
- {
- char c;
-
- puts("to decimal (d), hex (x) or octal (o) ?");
- switch ( getchar() )
- {
- case 'd':
- putd(n);
- break;
- case 'x':
- putx(n);
- break;
- case 'o':
- puto(n);
- break;
- default:
- puts("what?\n");
- }
- }
-
-
-
- NOTE NOTE:
-
- Standard C allows you to place the "default" word
- anywhere within the "switch", and program control
- will jump there only after all of the "case
- <constant expression> :"'s have been checked and
- no match found. Here again, SCI dares to be
-
-
- - 45 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- different! If a "default" is encountered before a
- matching "case", the program continues with the
- <statement> following the "default". Therefore,
- it is a good idea to always place your "default"
- statements at the end of the "switch".
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 46 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 13. Arrays
-
-
- An array in C is a block of contiguous memory locations
- (meaning they are located "one after the other" in memory)
- that all have the same type ("char" or "int") and can be
- accessed individually. These individual data items in an
- array are known as the array's "elements". You have already
- used arrays earlier, in your very first C program, namely
- the sequence of ASCII characters in the string "hello,
- world\n". The array's elements are the ASCII characters
- 'h', 'e', 'l', etc. This array however, could not be used
- to store any information other than that sequence of
- characters, just like the integer constant 5 let's say, can
- not be used to store a different number. In this section we
- will show you how to create and use arrays for data storage
- and retrieval.
-
- Arrays are declared in a similar fashion as simple
- variables, but following the array's name you must indicate
- how many elements the array will have. The size of an array
- is constant, once it has been declared it can not be
- changed. Below is an example of an array declaration
-
-
- char vartable[15], macnam[100];
-
-
- This statement declares two arrays that have 15 and 100
- elements respectively. The square brackets ([ and ])
- identifies the variable as being an array, they are also
- required when you wish to access one of the array's
- elements:
-
-
- c = vartable[ 5 ];
-
-
- In C, array elements are counted from zero instead of
- _____ one, so the above statement takes the sixth element of
- "vartable" and stores it in the variable "c". To access the
- _____ first element of the array, we would write:
-
-
- vartable[0] = 35;
-
- ____ Consequently, the last element of the array would be
- ___ "vartable[14]" and not "vartable[15]". In fact, if you did
- attempt to store a number in "vartable[15]", you would
- overwrite some unknown location in memory that was already
- being used as storage for another variable or, worse yet,
- that was part of your program code. The results of
-
-
- - 47 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- overrunning a C array like this are unpredictable and are
- dependent on the environment the C program is running in
- (the type of machine, the C compiler used, etc.)
-
- When the name of an array is used by itself without the
- square brackets as in:
-
-
- i = vartable + 5;
-
- it is taken to be a pointer to the first element of the
- array. In the example above, "i" would be assigned the
- ______ ________ ___ memory location of the sixth element in the array, and not
- ___ _____ the value of this memory location.
-
- The Library Function "gets()" reads a line of input
- from the console keyboard and places the characters at the
- address pointed to by its argument. This function waits for
- the user to hit a carriage return before it returns to the
- caller. The input line is always terminated with a zero
- byte by "gets()" and the carriage return is stripped out.
- This makes it suitable for printing by its partner,
- "puts()". Try the following program:
-
-
- greet()
- {
- char name[ 80 ];
-
- puts("hello, what's your name? ");
- gets( name );
- puts("nice to meet you, ");
- puts( name );
- puts(". Have a nice day!\n");
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 48 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 14. Pointers
-
-
- The last example above leads us directly into our next
- discussion. In C, you have the ability to access a memory
- location by "pointing" at it with a variable. This type of
- variable is known as a "pointer" in C.
-
-
- NOTE NOTE:
-
- The number of bytes of storage a pointer variable
- needs depends on the environment the C program is
- running in. All 8-bit personal computers (8080,
- z80, 6502, etc.) use 2 bytes for pointer
- variables. The IBM-PC which has an 8088 CPU may
- use either 2 or 4 bytes for pointer variables,
- depending on the C compiler used. SCI always uses
- 2 bytes.
-
-
- C knows about the type of variable being pointed at
- from the pointer's declaration:
-
-
- char *char_pointer;
- int *int_pointer;
-
-
- Above we declared two variables, "char_pointer" and
- "int_pointer". The asterisk (*) in the declaration
- statement identifies the variables as being pointers. the
- "char" and "int" keywords define the type of variable that
- the pointer points to. To illustrate:
-
-
- i = *int_pointer;
-
-
- _______ This would retrieve the integer (two bytes in SCI)
- ________ found at the memory location addressed by the contents of
- "int_pointer", and store it in "i". In this example, we
- can't tell what will be stored in "i" because we don't know
- what the contents of "int_pointer" is. Recall from an
- earlier discussion that SCI always initializes its variables
- to zero so in this case, "i" would contain the two bytes
- found in memory locations zero and one
-
- Unless you know what is in memory locations zero and
- one, this information is not very useful. The power of
- pointers lies in the fact that they can be made to point at
- an array, like this:
-
-
- - 49 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- char vartable[15], *cp;
-
- cp = vartable;
- c = *cp;
- cp = cp+1;
- d = *cp;
- cp = cp+1;
- e = *cp;
-
-
- Here we have set the character pointer "cp" to point at
- the first element of the array "vartable", and assigned the
- ________ contents of this first element to the variable "c" just by
- letting "cp" point at it. By simply adding one to "cp" we
- have made it to point at the next element in "vartable".
-
-
- 14.1. Lvalues and Rvalues Revisited
-
-
- Pointers are useful because they can be changed (bent?)
- to address any location in memory whereas arrays are fixed
- and always point to their first element. For example, if
- your tried to do this:
-
-
- char a[10];
-
- a = a + 1;
-
-
- ____ you would get an error message. The C language does
- ___ _____ ___ __ ______ ___ _____ __ __ _____ ________ not allow you to change the value of an array variable. If
- it did there might be the possibility of your program
- "forgetting" where the data in the array is located. Thus
- arrays can be put in the same category as strings and
- integer constants, namely "rvalues" (see an earlier
- discussion on Lvalues and Rvalues).
-
- The attempt to change the array variable "a" in the
- example above would therefore reward you with a "need an
- lvalue" error message from the SCI interpreter.
-
- Pointers on the other hand are more analagous to
- variables - they can be modified and are therefore
- considered to be "lvalues".
-
-
- 14.2. Pointer Operator
-
-
- The asterisk in the examples above is known as the
-
-
- - 50 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- "pointer" operator. This is a unary operator and is used in
- the same manner as the negation (-) unary operator. The
- pointer operation tells C to treat its associated pointer
- variable as a memory address and to store or retrieve the
- _______ data item at that address. Note that the operand of a
- pointer operator must have been declared as a pointer, or C
- will complain. This is in your own best interest because we
- humans tend to forget little details like this. For
- example, if you wrote:
-
-
- char c, d;
-
- d = *c;
-
-
- you would get a "not a pointer" error message from SCI.
-
- Now, try typing in this little program using the SCI
- editor:
-
-
- prints(s)
- char *s;
- {
- while(*s)
- {
- putchar(*s);
- s=s+1;
- }
- }
-
-
- The Library Function "putchar()" accepts a single
- argument and prints the ASCII representation of this
- argument to the console screen. This program takes a
- pointer to a character string as an argument. Then, while
- the character being pointed at is non-zero, "putchar()"
- prints the character onto the console. The pointer is then
- incremented (s=s+1) so that it points at the next character
- in the string. Now execute the following command from the
- shell:
-
-
- > prints("hello, world\n");
-
-
- This should have resulted in the words "hello, world"
- printed on the console.
-
- Except for the fact that pointers may be changed and
- arrays may not, C treats both of them identically. For
-
-
- - 51 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- example if we have the following two data declarations:
-
-
- char c, ca[ 10 ];
- char *cp;
-
- we can choose to view the array variable "ca" as a pointer,
- and the pointer variable "cp" as an array in our programs,
- like so:
-
-
- c = *ca;
- cp[ 5 ] = c;
-
-
- So, we could have written the sample program from
- before like this:
-
-
- prints(s)
- char *s;
- {
- int i;
-
- for ( i=0; s[i]; i=i+1 )
- putchar( s[i] );
- }
-
-
- This would have left the pointer argument "s" unaltered
- when the "for" loop was finished. This is sometimes
- necessary, as in the following example:
-
-
- prints10(s)
- char *s;
- {
- int i, j;
-
- for ( j=0; j<10; j=j+1 )
- {
- for ( i=0; s[i]; i=i+1 )
- putchar( s[i] );
- }
- }
-
- Here the string passed to "prints10()" is printed on the
- console ten times. If we had incremented the character
- pointer "s" instead of using the index "i", then the second
- time through the outer loop would have started with "s"
- pointing to the character after the end of the string.
-
-
-
- - 52 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 14.2.1. Pointer Expressions
-
-
- We stated that C knows the type of data item a pointer
- is pointing to ("char" or "int"). Being a rather dumb (and
- therefore compact) interpreter, SCI does not know what type
- of data item is being pointed at when the pointer is hidden
- in an expression somewhere, like so:
-
-
- i = *(ip + 5);
-
-
- In this example we are trying to retrieve the data item
- pointed at by "ip" and offset by 5. If "ip" was a pointer to
- an "int", we would think that we are retrieving the sixth
- 2-byte integer of the array pointed at by "ip". Physically,
- we are interested in the tenth and eleventh bytes in the
- array. The above expression, however, will retrieve only
- one byte for us (the sixth byte in the array) instead of
- two, and from the wrong place in the array. This is a
- limitation only of SCI - most C compilers are smart enough
- to know better and the above statement would have retrieved
- the sixth "int" from the array, as expected.
-
- On occasion you may wish to use integer constants as
- pointers, like this:
-
-
- char c;
-
- c = *0x0100;
-
-
- When a constant is used as the target of a pointer
- operation, SCI will access the two bytes (an "int") at the
- location specified by the value of the constant, in this
- case at location 100 hex. You can get as creative as you
- want when using constants as pointers:
-
-
- char c, offs;
-
- c = *((0x101 + offs) * 2);
-
-
- Note that SCI will allow you to use variables that have
- not been declared as pointers in pointer expressions if they
- are enclosed in parentheses as shown above.
-
-
-
-
-
- - 53 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- NOTE NOTE:
-
- Standard C does not allow you to use constant
- expressions as pointers, any attempt to do so will
- usually result in an error message.
-
-
-
- 14.2.2. printf()
-
- C functions do not intrinsically know whether their
- arguments are printable strings or just an array or numbers,
- like BASIC does. Therefore there are no C functions that
- are analogous to BASIC's PRINT statement which prints either
- a number or a string depending on its argument. The
- standard C Library Function "printf()", however, performs a
- print operation similar to BASIC's PRINT statement.
- "Printf", which is read as "print-eff", stands for "print
- formatted". It is the most unusual standard C function
- because it accepts a variable number of arguments, depending
- on the contents of its first argument. The first argument
- to "printf" is a string of characters and is known as the
- "control string". "Printf" works like this: it scans
- through the characters in the control string and prints them
- out on the console screen; if a percent symbol (%) is
- encountered in the control string, the character following
- ________ the "%" determines how printf's next argument will be
- processed. For example, if the character following the "%"
- is a lower case letter "s", the next argument is assumed to
- be a character string and is printed out to the console
- instead of the "%s" character combination. A "%d"
- combination in the control string takes its next argument to
- be an integer and prints its decimal value. "Printf" keeps
- track of which arguments have already been printed by a
- "%-letter" combination, so that the next "%-letter"
- combination affects the next argument in the argument list.
-
- For example, try the following command from the shell:
-
- printf("%s, did you know %d * %d is %d?\n","Bob",376,49,376*49)
-
- The "%s" conversion treats the second argument, "Bob"
- as a string (which it is!) and prints it; the first "%d"
- grabs the next argument in the list (376) and prints it as a
- decimal number; the second "%d" prints 49 as a decimal
- number; finally, the third "%d" takes the product of 376 and
- 49 and prints it as a decimal number. What should have
- appeared on your screen is:
-
-
- Bob, did you know 376 * 49 is 18424?
-
-
-
- - 54 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- "Printf" also recognizes these other conversion codes:
-
-
- x %x - prints its argument as a hexadecimal number. The
- characters "0x" do not appear in the printed
- number - you must add them if needed, like so:
-
-
- printf( "%d = 0x%x\n", 376, 376 )
-
-
- o %o - prints its argument as an octal number.
-
- c %c - prints its argument as an ASCII character.
-
-
-
- 14.3. Address Operator
-
-
- In an earlier discussion about the scope of variables,
- we said when a variable is passed to a function, the called
- function creates a clone of the caller's variable and copies
- its contents into this local variable. This way the
- function can not alter the contents of the caller's
- variable. How then can we have a function alter the
- contents of our local variables if it becomes necessary?
- There are several options open to us: 1) write the function
- so that it returns a value which we can then assign to our
- local variable, 2) have the function put the value into a
- global variable which we can access, or 3) pass the function
- the address of our local variable. The first option only
- allows the function to pass back one piece of information,
- thus limiting its usefulness. Option 2 is an acceptable
- method but forces the function to become more dependent upon
- the entire program structure. This is fine for
- application-specific functions, but severly restricts the
- modularity of general purpose functions. The accepted
- method is to pass the function the address of our variable
- using the "address operator", "&", like so:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 55 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- prog()
- {
- char c;
-
- func( &c ); # call "func()", pass the address of "c"
- }
- func( ptr )
- char *ptr;
- {
- *ptr = 12;
- }
-
-
- The ampersand (&) as used above is a unary operator
- _______ that tells C we want to use the address of the associated
- ________ __ _____ _____ ___ variable instead of its contents. In other words, the
- _______ ________ ______ _ _______ address operator yields a pointer to its associated
- operand. That's why we declared the argument to the
- function "func()" as a pointer to a "char". In the above
- example then, the local variable "c" in "prog()" would have
- been set to 12 after the call to "func()".
-
- Extreme caution must be exercised when passing pointers
- to variables like this. If we had instead declared "ptr" as
- a pointer to an "int" (int *ptr;) in the function, "func()",
- then the assignment "*ptr = 12;" would have destroyed the
- memory location following "c" and possibly caused the
- program to crash.
-
- Note that you may only use the address operator on
- lvalues; this means only simple variables and pointers. If
- you try to use the address operator on a constant, a string
- or an array you will evoke a "need an lvalue" error from
- SCI.
-
- Now enter and test this little program from the SCI
- interpreter:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 56 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- words()
- {
- char *word, *cp, linebuf[80];
-
- puts("type some words: ");
- gets(cp = linebuf);
- while(cp) {
- cp = parse(cp, &word);
- puts("word = <");
- puts(word);
- puts(">\n");
- }
- }
-
- parse(str,word)
- char *str;
- int *word;
- {
- while(*str==' ')
- ++str;
- *word=str;
- while(*str!=' ' && *str)
- ++str;
- if(*str==0)
- return 0;
- *str=0;
- return str+1;
- }
-
-
- and from the shell execute the function "words()". What
- does this program do?
-
-
- 14.3.1. scanf()
-
-
- The companion to the Library Function "printf()" is
- "scanf()" (read "scan-eff"). This function performs the
- reverse operation of "printf()", that is, it converts
- strings and numbers read from the console keyboard and
- places them into program variables. This means that its
- ________ arguments must be pointers to the appropriate data type.
-
- Examine the following program:
-
-
-
-
-
-
-
-
-
- - 57 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- getname()
- {
- char firstname[20], lastname[20];
- int zip;
-
- puts("What is your first, last name and zipcode?");
- scanf("%s %s %d", firstname, lastname, &zip );
-
- printf("firstname = %s\n",firstname);
- printf("lastname = %s\n",lastname);
- printf("zipcode = %d\n",zip);
- }
-
-
- Scanf assumes that a string is a stream of consecutive
- non-blank characters. The function will not stop reading
- input until all of its conversion (%-letter) codes have been
- satisfied, or until an end of input (control-Z in MS-DOS) is
- __ ___ encountered. This means that carriage returns do not cause
- scanf to quit reading and return to the caller. Carriage
- returns are simply treated as spaces and tabs, collectively
- known as "white space". In the program above, you could
- have entered you first and last name on seperate lines if
- you like, or on the same line seperated by one or more
- spaces or tabs.
-
- _______ Notice that the address of the integer variable "zip"
- was passed to scanf; do you now understand why? If we had
- used the following statement instead:
-
-
- scanf( "%d", zip );
-
-
- ________ then the contents of "zip" would have been used as the
- location where scanf would place an integer value. If the
- contents of "zip" had been zero, then memory locations zero
- and one would have been altered by scanf, and would have
- possibly damaged the operating system.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 58 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 15. Increment and Decrement Operators
-
-
- As we have already seen, we can use several methods to
- access elements in an array. Let's say we had an array and
- wanted to access its elements sequentially, one after the
- other. We could either declare a pointer to the array and
- then increment the pointer by one; or we could declare an
- integer variable to be used as an array index and increment
- it each time by one, like so:
-
-
- char a[10], *p;
- int i;
-
- # set all 10 elements in the array "a" to zero,
- # using a pointer:
- p=a;
- i=0;
- while(i<10)
- {
- *p = 0;
- p=p+1;
- i=i+1;
- }
-
- # ...and using an index:
- i=0;
- while(i<10)
- {
- a[i] = 0;
- i=i+1;
- }
-
- Since these operations comes up often in programming, the C
- language offers a very efficient method of incrementing and
- decrementing varibles. These are appropriately enough,
- called the "increment" and "decrement" operators, "++" and
- "--". The increment/decrement operators are unary operators
- and can appear either before or after a variable name, like
- this:
-
-
- int i;
-
- ++i; # increment "i" by one
- i++; # same thing
- --i; # decrement "i" by one
- i--; # and again
-
- ______ If the operator appears before the variable, it is known as
- _____ a "pre-" increment or decrement; if it appears after the
-
-
- - 59 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- variable, it is a "post-" increment/decrement. The
- ______ pre-inc/dec operators perform their function before the
- variable is used in the expression, whereas the post-inc/dec
- _____ operators perform their function after the variable has been
- used in the expression. This is best explained with an
- example:
-
-
- incdec()
- {
- int i;
-
- i = 0;
- putd( ++i );
-
- i = 0;
- putd( i++ );
- }
-
-
- The first instance of "putd()" would print a 1 - the
- variable "i" was increment by one before its value was
- passed to "putd()". Now we set "i" to zero again and the
- second call to "putd()" will print a 0. This is because the
- post-increment operator passes the value of "i" to the
- ______ function "putd()" before it gets incremented by one.
-
- These operators are very handy for quickly scanning
- through an array like this:
-
-
- prints(s)
- char *s;
- {
- while ( *s )
- putchar( *s++ );
- }
-
-
- When using the increment/decrement operators on
- pointers, SCI knows what data type the pointer is
- referencing and adjusts the pointer so that it points to the
- next/previous data item. In other words, when incrementing
- a pointer to an integer, the pointer is incremented by 2
- instead of one so that it points to the next integer. If we
- wanted to print out all the numbers in an integer array, we
- might do something like this:
-
-
-
-
-
-
-
- - 60 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- dump()
- {
- int array[ 10 ], *ap;
-
- ap = array;
- for ( i=0; i<10; ++i )
- printf( "%d\n", *ap++ );
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 61 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 16. A Tour Through the File I/O Functions
-
-
- The formal definition of the C language does not really
- include any of the Library Functions we have discussed so
- far. However, most of these have become defacto standards
- and are considered a part of the language's support
- library. Although the exact usage of support functions may
- vary from one compiler implementation to the next, most C
- compilers adhere to a "standard" to some degree. This
- section discusses SCI's implementation of the file I/O
- functions which is fairly compatible with the "standards"
- proposed by the authors of the C language.
-
- This section only attempts to clarify some points
- concerning the file I/O functions and is not meant as a
- reference. Please refer to the section in the User's Manual
- titled "The Library Function" for exact details about these
- functions.
-
-
-
- 16.1. fopen()
-
-
- Before a file can be used (read from or written to), it
- must first be "opened" with the Library Function "fopen()".
- Opening a file ensures that the file exists and is
- readable/writable and prepares internal data structures for
- dealing with the file. Before a C program starts up, three
- "files" are opened for it by the "operating system", these
- are known as the "standard input", "standard output" and
- "standard error" file. These usually default to the user's
- console keyboard and screen. The "standard error" file is
- an output file and always defaults to the user's console
- screen. It is usually used by the program to display error
- messages. The reason we like to have two output files is
- because we don't want to intermix program error and
- informational messages with program output data, and to
- insure that error messages always appear on the user's
- console.
-
- To open a file, "fopen()" must be called with two
- arguments: the file name, and a character string that
- defines how the file is to be accessed. For example,
-
-
- int channel;
-
- channel = fopen( "SHELL.SCI", "r" );
-
- would open the file "SHELL.SCI" for reading ("r"). MS-DOS
-
-
- - 62 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- allows the file name to be in upper or lower case, other
- operating systems may not be so indifferent about file
- names. The second string, known as the "open mode", must
- contain either a lower case "r" to open the file for
- reading, "w" for writing or "a" for appending. When a file
- is opened for reading, it must already exist or "fopen()"
- will return an error code. If a file is opened for writing,
- it may or may not exist; if it does exist, it is first
- deleted before the open. If a file is opened for appending
- and the file exists, it is opened for writing, but data is
- written to the current end of the file. If the file does
- not exist, an open for append acts like an open for write.
- You may also open a file for both reading and writing,
- meaning you may intermix read and write functions on the
- same file, but see the section on the Library Functions for
- more information.
-
- The value returned by "fopen()" is an integer known as
- the "channel number" and points to the previously mentioned
- internal file control data structure. This channel number
- is then used by the other file read/write functions to
- access the file. If "fopen()" was unable to find the file
- (file opened for reading) or the file could not be created
- (file opened for writing), it returns a zero, indicating
- failure.
-
- The special channel numbers 1, 2 and 3 may be used to
- read and write from/to the standard input, output and error
- files. These channel numbers may be used with the file
- read/write routines at any time, unless of course you have
- closed these channels.
-
-
- 16.2. fclose()
-
- When a file is no longer needed, it should be "closed" by
- the program. Closing a file ensures that the file is safely
- stored on disk and it frees up the internal file control
- data structure.
-
- This function expects a single argument, the file
- pointer:
-
-
- fclose( fp );
-
-
- and returns a zero if the file was closed successfully, or a
- -1 if an error occured (the file was never opened, disk is
- write protected, etc.).
-
-
-
-
- - 63 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 16.3. fgetc() and fputc()
-
-
- The functions "fgetc()" and "fputc()" are used to read
- and write respectively a single character from/to a file.
- Both of these functions advance a "file position pointer"
- which points to the next character to be read/written
- to/from the file. This file position pointer is one of the
- items in the aforementioned internal file control data
- structure. These functions are used like so:
-
-
- copy(fromfile,tofile)
- char *fromfile, *tofile;
- {
- int fromchannel, tochannel, c;
-
- fromchannel = fopen( fromfile, "r" );
- tochannel = fopen( tofile, "w" );
- while ( (c = fgetc( fromchannel )) != -1 )
- fputc( c, tochannel );
- fclose( fromchannel );
- fclose( tochannel );
- }
-
-
- This little program copies the file whose name is the
- string at "fromfile" to the file whose name is at "tofile".
-
- The function "fgets()" returns the character that was
- read from the file (a single byte value from 0 to 255) or a
- minus one if the end of the file was reached or if some
- other error occured. The function "fputc()" returns the
- character that was written or a minus one if an error
- occured.
-
-
- 16.4. fgets() and fputs()
-
-
- You may also read and write disk files a "line" at a
- time with the functions "fgets()" and "fputs()". A "line" is
- a sequence of characters in the file that end with a newline
- ("\n") character. These are similar to the functions
- "gets()" and "puts()", which read and write from/to the
- standard input and standard output, with the exception that
- the newline character is copied into the line input buffer
- by "fgets()" and a newline is automatically appended to
- every string written out to the file by "fputs()".
-
- Note that the two functions fputs("hello",2) and
- puts("hello") are identical.
-
-
- - 64 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 16.5. fread() and fwrite()
-
-
- Sometimes it is useful to be able to read or write a
- file in arbitrarily long "blocks". For example, suppose we
- wanted to store an array of integer numbers in a file. The
- character read/write functions ("fgetc()" and "fputc()")
- would work but would be less efficient than writing several
- characters at a time. The functions "fread()" and
- "fwrite()" are ideal for these situations:
-
-
- sortfile()
- {
- int array[ 100 ];
- int channel;
-
- channel = fopen( "NUMBERS.DAT", "r" );
- fread( array, 200, channel );
- fclose( channel );
-
- sort( array, 100 );
-
- channel = fopen( "NUMBERS.DAT", "w" );
- fwrite( array, 200, channel );
- fclose( channel );
- }
-
- sort(a,n)
- int a[], n;
- {
- int temp, i, j;
-
- for ( i=0; i<n-2; ++i ) {
- for ( j=i; j<n-1; ++j ) {
- if ( a[j] > a[j+1] ) {
- temp=a[j];
- a[j]=a[j+1];
- a[j+1]=temp;
- }
- }
- }
- }
-
- This program reads an array of 100 numbers from a file,
- sorts them in numeric order and writes them back to the
- file. Note that we asked "fread()" and "fwrite()" for 200
- bytes. Since the array consists of 100 integers and each
- integer is 2 bytes, the array is 200 bytes long.
-
- Also, be sure to close a file when it is no longer
- needed. If we had neglected to close the file after the
-
-
- - 65 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- "fopen()" for reading, the second call to "fopen()" would
- have altered our file pointer. The value of the first file
- pointer would have been destroyed and the internal file
- control data structure would have been lost in limbo
- forever. Although this wouldn't have caused any damage, it
- is very sloppy programming. Keep in mind that you have only
- 10 file control data structures available.
-
-
- 16.6. fseek() and ftell()
-
-
- All of the file read/write functions advance an
- invisible "file position pointer" which determines where in
- the file the next character will be read from or written
- to. Sometimes it is necessary to re-read a character or
- group of characters in a file, or to write over the current
- contents in a file with new data. The function "fseek()"
- can be used to relocate the file position pointer to
- anywhere within the file, and allow you to re-read or
- re-write data in the file as necessary.
-
- Ftell simply returns the current value of the file
- position pointer.
-
- Examine the following sample program:
-
-
- link()
- {
- int start, current, inchannel, outchannel, c;
-
- inchannel = fopen( "RAW.DAT", "r" );
- outchannel = fopen( "LINKED.DAT", "wr" );
-
- start = 0;
- fwrite( &start, 2, outchannel );
- while ( (c=fgetc( inchannel )) != -1 ) {
- if ( fputc( c, outchannel ) == 0x00ff )
- current = ftell( outchannel );
- fseek( outchannel, start, 0 );
- fwrite( ¤t, 2, outchannel );
- fseek( outchannel, current, 0 );
- start = current;
- }
- }
- fclose( inchannel );
- fclose( outchannel );
- }
-
- This program copies the file RAW.DAT to LINKED.DAT. Each
- time a byte of all one's (FF hexadecimal) is encountered in
-
-
- - 66 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- RAW.DAT, the program backs up to the previous start location
- in LINKED.DAT (indicated in the variable "start") and
- inserts the file's current file position pointer
- ("current"). In other words, the program creates a file
- identical to RAW.DAT except that the data in the file
- contains information that tells where all of the 0xff's are
- located within the file - a linked list.
-
- Since SCI only supports integer variables, this limits
- the maximum range of absolute file positioning available
- with "fseek()" to 32767 from the beginning or end of the
- file. You can however, position the file pointer to within
- _______ +/-32767 bytes from the current position. This allows you
- to position the file pointer anywhere within the file, no
- matter how large the file is. Standard C uses "long" data
- variables instead of "int"'s for specifying the file
- position offset. Long's are usually twice the size of an
- "int" (four bytes instead of two), which gives you a much
- larger range of absolute file positioning.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 67 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 17. The Debuger
-
-
- SCI provides a powerful program debugging facility that
- allows you to execute your programs with complete control.
- When the debuger is active, it takes control of your program
- and allows you to step through the program in a controlled
- fashion. You have the option of either executing a line at
- a time, or stop at any line in the program. From the
- debuger you can also examine and change program variables in
- the middle of a program run, or execute any valid C
- statement.
-
- Besides being a reference for the SCI debug facility,
- this section will introduce you to general debugging
- strategies, and show you how you can use the SCI debuger to
- gain a better knowledge of C program flow.
-
-
- 17.1. Introduction
-
-
- The SCI debuger operates in what is known as "symbolic"
- mode. As you probably already know, a computer can not
- directly execute program instructions written in the C (or
- any other higher-level) language. The C language
- instructions must first be converted to machine language and
- then executed by the computer. This is the mode of
- operation when using a C compiler. Alternatively, the C
- source code can be directly executed by a program known as
- an "interpreter", which is exactly how SCI works. A C
- program that has been compiled is completely unreadable by
- us humans - all resemblence to the original C code has been
- stripped from the program since it is intended only for the
- computer's "eyes". We say that the "symbolic" representation
- of a machine readable program has been removed. On the
- other hand, since an interpreter always keeps a "symbolic"
- (human readable) form of your program in memory, it is very
- easy to follow the program as it is being executed by the
- interpreter. A debuger that has this ability to let the
- human reader follow along as the program is executed by the
- computer, is known as a "symbolic debuger".
-
- There are basically two types of program errors that
- can occur: unrecoverable and recoverable. Unrecoverable
- errors are typified by the computer's refusal to answer to
- the programmer's desperate pounding on the keyboard - we say
- that the computer has "locked up" and gone south for the
- winter. These errors may be caused by partial or complete
- destruction of the program itself, or of the operating
- system and usually require you to turn the computer off and
- then on again. Recoverable errors are the kind which do not
-
-
- - 68 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- allow the program to run to normal completion and return you
- to either the operating system or to the calling program, or
- simply "get stuck" in a never ending loop. The category of
- recoverable errors also include incorrect results:
-
-
- YOU: what is 2 plus 2?
- COMPUTER: 5
-
-
- and unexpected results:
-
-
- COMPUTER: shall I delete this file?
- YOU: No
- COMPUTER: OK, file deleted!
-
-
- For obvious reasons, SCI's built-in debuger is only
- capable of dealing with recoverable errors.
-
- The apporach to finding both of these types of errors
- is basically the same: allow the program to run normally up
- to the point just before it goes berzerk, then stop and look
- at how it got there. Usually, the hardest task is finding
- that point where your program goes over the edge. You have
- two choices: either run the program from the very beginning,
- one line at a time until something unexpected happens, or
- allow the program to run normally and stop just before the
- section of code that is suspect. The method of executing a
- program a line at a time is known as "single-stepping".
- Running a program normally and having it stop at a given
- line is known as "running to breakpoint". The SCI debuger
- allows you to use both of these approaches in any
- combination.
-
-
- 17.2. Enabling the Debuger
-
-
- The Library Function "trace" is used to turn the SCI
- debuger on and off and may be called either from the shell
- prompt or from within your program. A single argument to
- "trace" determines whether the debuger will be turned on or
- off: if the argument is non-zero the debuger is turned on;
- if zero, it is turned off. Thus, your program would look
- like this:
-
-
-
-
-
-
-
- - 69 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- func()
- {
- int i;
- .
- .
- .
- trace(1); # turn debuger ON
- while(i<10) # scrutinize this loop
- {
- .
- .
- .
- }
- trace(0); # turn debuger OFF again
- }
-
-
- You can also turn the debuger on directy from the
- keyboard while a program is running. By pressing the
- <ESCAPE> key at any time, the program is stopped in mid
- execution and the debuger is turned on. Thus, if you have a
- program that seems to be stuck in a forever loop, you can
- get control and take a look at what's causing the problem.
-
- When the debuger gets control of your program, it will
- automatically display the line number and program text of
- the next line to be executed. It is important to realize
- that the displayed program line has not yet been executed.
- Directly below the displayed line is a circumflex that
- points the first item in the line that will be executed.
- For example, if you had more than one statement on a single
- line, you might see:
-
-
- 12: i = 10; putd(i);
- ^
-
-
- The debuger then displays its question mark (?) prompt
- and waits for you to enter a command. Debuger commands
- always start with a dot (.) in the first column, followed by
- a command mnemonic letter. You can also enter a C statement
- at the debuger's "?" prompt and have it evaluated and the
- results displayed, just like in the shell.
-
- We will now walk through a sample program using the
- debuger as a way of introducing you to the debuger
- commands.
-
-
-
-
-
-
- - 70 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 17.3. Sample Debug Session
-
-
- If you haven't done so already, list the sample program
- that came with your distribution disk, CALC.SCI, either on
- your printer or "TYPE" it out on your screen. This is a
- simple integer calculator program that does addition,
- subtraction, multiplication and division.
-
- At the shell prompt, load CALC.SCI then type "calc()"
- to start the program. The program displays its prompt (->)
- and waits for you to enter a command. Try entering some
- mathematical expressions:
-
-
- > load calc.sci
- > calc()
- -> 2+2
- 4
- -> 2+3*4
- 14
- -> 3*4+2
- 14
- -> 2-30/2
- -13
-
-
- Notice that the program is smart enough to know that
- multiplication and division have higher precedence than
- addition and subtraction. To get out of the program and
- back to the shell, type an "x":
-
-
- -> x
- 0
- >
-
-
- Now, let's try the same scenario but this time turn on
- the SCI debuger before you start the calculator:
-
-
- > trace(1)
- 0
- > calc()
- cmd:calc()
- ^
- ?
-
-
- The debuger displays the C statement you entered on the
- shell's command line and prompts you with its "?" prompt
-
-
- - 71 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- and waits for you to enter a command. For now, just type a
- few <RETURN>'s:
-
-
- ?
- 4:calc()
- ^
- ?
- 5:{
- ^
- ?
- 6: char line[ 80 ];
- ^
- ?
- 8: Stacktop = 10;
- ^
- ?
- 9: while(1)
- ^
- ?
-
-
- The program is being executed one line at a time each
- time a <RETURN> is hit. Notice that each line is displayed
- preceded by the line number of the program. The statement
- you entered from the shell that started up the calculator is
- not a part of the program, and instead was preceded with a
- "cmd:" to indicate this fact.
-
-
- 17.3.1. Exiting the Debuger
-
-
- To halt the program and return back to the shell, use
- the debuger's "quit" command:
-
-
- ? .q
- 0
- >
-
-
- Now we're back to the shell's prompt. The ".q" command
- stopped the program and turned the debuger off. Since we
- want to experiment some more, turn the debuger back on again
- and start up the program:
-
-
-
-
-
-
-
-
- - 72 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- > trace(1)
- 0
- > calc()
- cmd:calc()
- ^
- ?
-
-
-
- 17.3.2. Single Stepping
-
-
- You can execute more than one program line at a time
- with the "step" command. At the "?" prompt, type ".s"
- followed by the number of program lines you want to execute:
-
-
- ? .s 4
- 9: while(1)
- ^
- ?
-
-
- The debuger executed 4 lines, then stopped. This is
- useful when you want to get through a section of code
- quickly without having to hit <RETURN> and waiting for each
- line to be displayed. The "continue" command is equivalent
- to a ".s" with an infinitely large step count:
-
-
- ? .c
- -> 2+2
- 4
- ->
-
-
- The program appears to be running slower than when it
- was run with the debuger turned off. This is because the
- debuger is still in control of the program, and must examine
- each line before it is executed. The usefulness of the ".c"
- command will become apparent later when we discuss
- breakpoints. To return back to the debuger prompt, hit an
- <ESCAPE> while the program is running. If the program is
- waiting for input from the console, hitting the <ESCAPE> key
- will have no effect. So type some mathematical expression
- as before, hit a <RETURN> and then quickly hit the <ESCAPE>
- key:
-
-
-
-
-
-
-
- - 73 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- -> 2+3*4
- interrupt
- 36: while(1)
- ^
- ?
-
-
- When the <ESCAPE> is hit, the debuger displays an
- "interrupt" message, followed by the program line it was
- currently working on.
-
-
- 17.3.3. Displaying Global Variables
-
-
- At any time the debuger is waiting for input you may
- display all of the program's global variables and their
- contents with the "global" command:
-
-
- ? .g
- char *Lineptr (0xc334) = "2+3*4"
- int Stack[10] (0xc320) = 4 2 0 0 0 0 0 0 0 0
- int Stackpt = 0
- int Stackto = 10
- ?
-
-
- The ".g" command displays each variable along with its
- data type ("char" or "int"). If the variable is an array or
- a pointer, its address is also printed in hexadecimal, for
- example (0xc352). Following that, the variable's value is
- displayed. If the variable is an array or a pointer, the
- first ten items in the array are displayed. Character
- arrays are printed as strings, integer arrays as a series of
- decimal numbers.
-
- Another form of the ".g" command, ".G", will display
- all of the program's functions and their program line
- numbers in addition to global variables:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 74 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- ? .G
- char *Lineptr (0xc334) = "2+3*4"
- int Stack[10] (0xc320) = 4 2 0 0 0 0 0 0 0 0
- int Stackpt = 0
- int Stackto = 10
- 4:calc()
- 22:number()
- 32:addition()
- 57:multiplication()
- 82:push( n )
- 89:pop()
- 96:isdigit( c )
- ?
-
-
-
- 17.3.4. Breakpoints
-
-
- Next we will discuss one of the most powerful features
- of the debuger: breakpoints. Let's say we wanted to stop
- the program every time the functions "push" and "pop" were
- called, so that we could inspect the program's state.
- Looking at the debuger's output from the ".G" command above,
- we see that these functions are located at lines 82 and 89
- respectively. To set breakpoints at these line numbers, we
- would enter the following two commands:
-
-
- ? .b 82
- breakpoint set:
- 82:push( n )
- ? .b 89
- breakpoint set:
- 89:pop()
- ?
-
-
- The debuger prints the program line at which the
- breakpoint is set for verification.
-
- You may set a maximum of 5 breakpoints at any one
- time. To display all of the breakpoints that are currently
- set, use the ".B" command:
-
-
- ? .B
- 82:push( n )
- 89:pop()
- ?
-
-
-
-
- - 75 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- Now we can continue executing the program normally and
- it should stop as soon as either the "push" or "pop"
- functions are called. This is where the ".c" command is
- used:
-
-
- ? .c
- breakpoint:
- 82:push( n )
- ^
- ?
-
-
- As soon as a breakpoint is reached, the debuger
- announces this fact and displays the program line at the
- breakpoint. To delete a breakpoint, use the "delete
- breakpoint" command:
-
-
- ? .d 82
- breakpoint deleted:
- 82:push( n )
- ?
-
-
- Again, the program line at which the breakpoint was
- deleted is displayed for verification. When a breakpoint is
- deleted, the debuger will no longer stop at this program
- line after a ".c" command.
-
- To delete all breakpoints set, use the ".D" command:
-
-
- ? .D
- all breakpoints deleted
- ?
-
-
-
- 17.3.5. Function Call Trace Back
-
-
- Using breakpoints we can be certain of only one fact:
- the program started at point "A", and stopped at point "B".
- We know nothing about the route it took in getting there.
- The debuger's "trace back" command at least tells us the
- order of function calls that got us to point "B": Continuing
- with our debuging session, type the following command:
-
-
-
-
-
-
- - 76 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- ? .t
- 82:push( n )
- 22:number()
- 57:multiplication()
- 32:addition()
- 4:calc()
- ?
-
-
- The function call trace back printed by the ".t"
- command is read backwards from bottom to top. In other
- words in the above display, the function "calc" (which is
- the starting point) called "addition", which in turn called
- "multiplication", and so on.
-
- A variation of the "trace back" command, ".T", will
- also display all local variables and their contents for each
- function in the trace back:
-
-
- ? .T
- 82:push( n )
- int n = 2
- 22:number()
- 57:multiplication()
- int num = 0
- 32:addition()
- int num = 0
- 4:calc()
- char line[80] (0xc334) = "2+3*4"
- ?
-
-
- The local variables are displayed in a similar format
- as for the ".g" command.
-
-
- 17.3.6. Examine a Program
-
-
- You may also use the SCI editor to examine your
- program. The editor will not allow you make any changes
- when invoked from the debuger, since this could completely
- alter the state of the current program run. To "examine"
- your program, type:
-
-
- ? .e
-
-
- The screen is erased and the editor is started up with
- the cursor resting on the line in the program that is to be
-
-
- - 77 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- executed next. You may move about freely in the editor, but
- you may not make any changes.
-
- When you exit the editor (with a ^Z) the debuger knows
- which line the cursor was on when you left the editor, and
- you may set a breakpoint at that line by just giving the
- _______ ".b" command without a line number.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 78 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 18. The Shell
-
-
- This section will discuss in detail the operation of
- the program found in the file SHELL.SCI. We will also show
- you how to customize the shell program to suit your needs.
-
- If you haven't done so already, print out the shell
- program file or "TYPE" it out on your console screen. As
- you can see there are basically 2 sections of this program:
- the first section declares all of the Library Functions
- ("sys" call interfaces), The second section starts
- immediately after the "entry" keyword with the function
- "main()". This is the function that is executed after
- SHELL.SCI has been loaded into memory. Let's examine this
- function more closely now:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 79 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- 43: entry
- 44:
- 45: char ln[80];
- 46: char pr[20000];
- 47:
- 48: main()
- 49: {
- 50: int f, t;
- 51:
- 52: puts(sys(0));
- 53: puts("\nShell V1.3 15Mar86 Copyright (C) 1986 Bob Brodt\n");
- 54: _nr=25; _nc=80;
- 55: _ro=_co=1;
- 56: _cp="\033[%d;%dH";
- 57: _el="\033[K";
- 58: _mhz=5;
- 59: *pr='Z';
- 60:
- 61: for(;;) {
- 62: puts("> ");
- 63: ln[5]=0;
- 64: if(gets(ln)) {
- 65: if (!strncmp(ln,"edit",4))
- 66: sys(atoi(ln+4),pr,19);
- 67: else if (!strncmp(ln,"list",4)) {
- 68: f=1;
- 69: t=32765;
- 70: if(ln[4])
- 71: sscanf(ln+4,"%d %d",&f,&t);
- 72: sys(pr,f,t,27);
- 73: }
- 74: else if (!strncmp(ln,"save",4))
- 75: sys(ln+5,pr,26);
- 76: else if (!strncmp(ln,"load",4))
- 77: sys(ln+5,pr,25);
- 78: else if (!strcmp(ln,"exit"))
- 79: exit();
- 80: else
- 81: printf("\n%d\n",sys(ln,pr,16));
- 82: }
- 83: }
- 84: }
-
-
- Note that we have included line numbers here for
- reference.
-
- The data declarations at lines 45 and 46 are the
- shell's input line buffer (ln[80]) and user program buffer
- (pr[20000]) respectively. Lines 52 and 53 of course print
- the program identification banners. Lines 54 through 58
- assign the editor's customization variables (for the IBM PC
-
-
- - 80 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- in this version). Line 61 starts a "forever" loop that can
- only terminated by the call to the function "exit()" at line
- 79. This loop starts out by displaying the shell prompt (">
- ") on the console screen, then waiting for a line of input
- from the console keyboard. The input line buffer stores the
- C statement read in from the console at line 64. It is then
- compared to each of the strings "edit", "list", "save",
- "load" and "exit". If the first four characters in the
- input buffer don't match any of these strings, the line is
- assumed to be a C statement and handed off to the
- interpreter (via "sys" function 16) for execution.
-
- The program buffer, "pr" is used to store the user's
- program functions and variables. The user can enter data
- into this buffer only by way of the SCI program editor
- ("sys" function 19). In fact, the program buffer is
- completely hidden from the user - any attempt to reference
- it via a C statement (for example "putchar( pr[0] )") will
- result in an "undefined symbol" error message. The user's
- program in the "pr" buffer is in "tokenized" form. That is,
- each language element (variables, keywords, punctuation,
- etc.) has been encoded so that it can be more easily and
- quickly recognized by the interpreter. The tokenized form
- of a program bears almost no resemblance to the
- human-readable form and should not be tampered with.
-
-
- 18.1. Customizing the Shell
-
-
- Now we will show you how you can customize this
- program. Start up SCI and when the shell's prompt appears,
- enter the command:
-
-
- > load shell.sci
-
-
- to load the shell file. Now edit the program from SCI's
- editor and remove all lines from the beginning of the
- program up to and including the "entry" keyword. Next let's
- change the string on line 62 (above) to something like:
- "yes, dear? ". Finally, replace the "exit()" on line 79
- with a "return".
-
- Exit the editor and from the shell prompt type:
-
-
- > main()
-
-
- You should see the program identification banner again
-
-
- - 81 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- and the new shell's prompt, "yes, dear? "! You can now do
- everything from this new shell that you did from the
- original shell - write programs with the editor, save them,
- list them and load them. When you type "exit" to this new
- shell however, you are returned to the original shell
- because we replaced the "exit to DOS" function call with a
- "return" to caller. Since the caller was the original
- shell, you are returned to its "> " prompt.
-
- Type an "exit" now to get back to the first shell and
- from there do a "save newshell". Then type "main()" again
- to get the "yes, dear? " shell. From here, type "load
- newshell" to load the newshell program. Edit the newshell
- program and change the "yes, dear? " prompt to something
- like: "you again? ". Now exit the editor and at the "yes,
- dear? " prompt type "main()". You should again see the
- program logon banner and the new shell prompt "you again?
- ", like this:
-
-
- yes, dear? main()
- Small C Interpreter V1.3 15Mar86 Copyright (C) 1986 Bob Brodt
- Shell V1.3 15Mar86 Copyright (C) 1986 Bob Brodt
- you again?
-
-
- In all, we now have 3 different shell programs running,
- one on top of the other, and we could actually continue
- doing this until we run out of memory! This is exactly
- analagous to the layers of an onion: each layer gets smaller
- as you go towards the center of the onion, just as the
- amount of usable memory becomes less as each new shell
- program is loaded from the previous shell.
-
- Now return to the original shell like so:
-
-
- you again? exit
- 0
- yes, dear? exit
- 0
- >
-
-
- It now becomes an easy task to customize the shell
- program to your heart's content using the SCI editor, test
- it from the SCI interpreter environment and when it's fully
- debuged, save it to disk. Of course you must remember to
- insert the Library Function declarations and the "entry"
- keyword before the new shell "main()" function if you intend
- to replace SHELL.SCI with the new program.
-
-
-
- - 82 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- As an exercise, add a directory listing command to the
- shell, using the following program as a guide. If are
- having difficulty, you may peek at the answers on the next
- page...
-
-
- dir(pat)
- {
- char file[16];
- int t;
-
- t=0;
- if(pat)
- f=scandir(pat,file);
- else
- f=scandir("*.*",file);
- while(f) {
- printf("%-20s",file);
- ++t;
- f=scandir(0,file);
- }
- if(t%4)
- putchar('\n');
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 83 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- .
- .
- .
- exit();
- else if (!strncmp(ln,"dir",3)) {
- char file[16];
-
- t=0;
- if(ln[3]) f=scandir(ln+4,file);
- else f=scandir("*.*",file);
- while(f) {
- printf("%-20s",file);
- ++t;
- f=scandir(0,file);
- }
- if(t%4) putchar('\n');
- }
- else
- .
- .
- .
-
-
-
- 18.2. DOS Command Line Arguments to the Shell
-
-
- A mechanism has been provided to pass operating system
- command line arguments to the shell in a way similar to most
- commercially available C compilers. By specifying a "-A" on
- the MS-DOS command lines, all arguments to the right of the
- "-A" will be ignored by SCI and instead passed to the shell
- program.
-
- SCI always passes two arguments to the "entry" program
- in the startup file, although the program is free to use or
- ignore these arguments. These are: a count of the number of
- arguments following the "-A" option on the DOS command line
- and; A pointer to the array of strings that contain these
- arguments. These arguments are commonly declared as "argc"
- (argument count) and "argv" (argument vector) in the C
- community.
-
- Make the following changes and additions to the program
- in SHELL.SCI:
-
-
-
-
-
-
-
-
-
- - 84 -
-
-
- Small C Interpreter Programmer's Manual
-
-
- .
- .
- .
- main(argc, arv)
- int argc, *argv;
- {
- int i;
-
- while ( i<argc )
- {
- puts(argv[i++]);
- putchar('\n');
- }
- .
- .
- .
- }
-
-
- Then, when the following command is entered at the
- operating system level:
-
-
- A>SCI -A Hello out there!
-
-
- the shell program would start up like this:
-
-
- Hello
- out
- there!
- Small C Interpreter V1.3M 15Mar86 Copyright (C) 1986 Bob Brodt
- Shell V1.3M 15Mar86 Copyright (C) 1986 Bob Brodt
- >
-
-
- Note that since SCI does not support pointers to
- pointers, the "argv" variable must be declared as a pointer
- to "int". Then, each "argv[i]" is a pointer to a string that
- points to one of the words passed on the command line.
-
-
-
-
-
-
-
-
-
-
-
-
-
- - 85 -
-
- əəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəə